VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VBoxHDD.cpp@ 95

Last change on this file since 95 was 1, checked in by vboxsync, 55 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 150.2 KB
Line 
1/** @file
2 *
3 * VBox storage devices:
4 * VBox HDD container implementation
5 */
6
7/*
8 * Copyright (C) 2006 InnoTek Systemberatung GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 */
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DRV_VBOXHDD
27#include <VBox/VBoxHDD.h>
28#include <VBox/pdm.h>
29#include <VBox/mm.h>
30#include <VBox/err.h>
31
32#include <VBox/log.h>
33#include <iprt/alloc.h>
34#include <iprt/assert.h>
35#include <iprt/uuid.h>
36#include <iprt/file.h>
37
38#include <string.h>
39
40#include "Builtins.h"
41
42/*******************************************************************************
43* Constants And Macros, Structures and Typedefs *
44*******************************************************************************/
45/** The Sector size.
46 * Currently we support only 512 bytes sectors.
47 */
48#define VDI_GEOMETRY_SECTOR_SIZE (512)
49/** 512 = 2^^9 */
50#define VDI_GEOMETRY_SECTOR_SHIFT (9)
51
52/**
53 * Harddisk geometry.
54 */
55#pragma pack(1)
56typedef struct VDIDISKGEOMETRY
57{
58 /** Cylinders. */
59 uint32_t cCylinders;
60 /** Heads. */
61 uint32_t cHeads;
62 /** Sectors per track. */
63 uint32_t cSectors;
64 /** Sector size. (bytes per sector) */
65 uint32_t cbSector;
66} VDIDISKGEOMETRY, *PVDIDISKGEOMETRY;
67#pragma pack()
68
69/** Image signature. */
70#define VDI_IMAGE_SIGNATURE (0xbeda107f)
71
72/**
73 * Pre-Header to be stored in image file - used for version control.
74 */
75#pragma pack(1)
76typedef struct VDIPREHEADER
77{
78 /** Just text info about image type, for eyes only. */
79 char szFileInfo[64];
80 /** The image signature (VDI_IMAGE_SIGNATURE). */
81 uint32_t u32Signature;
82 /** The image version (VDI_IMAGE_VERSION). */
83 uint32_t u32Version;
84} VDIPREHEADER, *PVDIPREHEADER;
85#pragma pack()
86
87/**
88 * Size of szComment field of HDD image header.
89 */
90#define VDI_IMAGE_COMMENT_SIZE 256
91
92/**
93 * Header to be stored in image file, VDI_IMAGE_VERSION_MAJOR = 0.
94 * Prepended by VDIPREHEADER.
95 */
96#pragma pack(1)
97typedef struct VDIHEADER0
98{
99 /** The image type (VDI_IMAGE_TYPE_*). */
100 uint32_t u32Type;
101 /** Image flags (VDI_IMAGE_FLAGS_*). */
102 uint32_t fFlags;
103 /** Image comment. (UTF-8) */
104 char szComment[VDI_IMAGE_COMMENT_SIZE];
105 /** Image geometry. */
106 VDIDISKGEOMETRY Geometry;
107 /** Size of disk (in bytes). */
108 uint64_t cbDisk;
109 /** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) */
110 uint32_t cbBlock;
111 /** Number of blocks. */
112 uint32_t cBlocks;
113 /** Number of allocated blocks. */
114 uint32_t cBlocksAllocated;
115 /** UUID of image. */
116 RTUUID uuidCreate;
117 /** UUID of image's last modification. */
118 RTUUID uuidModify;
119 /** Only for secondary images - UUID of primary image. */
120 RTUUID uuidLinkage;
121} VDIHEADER0, *PVDIHEADER0;
122#pragma pack()
123
124/**
125 * Header to be stored in image file, VDI_IMAGE_VERSION_MAJOR = 1.
126 * Prepended by VDIPREHEADER.
127 */
128#pragma pack(1)
129typedef struct VDIHEADER1
130{
131 /** Size of this structure in bytes. */
132 uint32_t cbHeader;
133 /** The image type (VDI_IMAGE_TYPE_*). */
134 uint32_t u32Type;
135 /** Image flags (VDI_IMAGE_FLAGS_*). */
136 uint32_t fFlags;
137 /** Image comment. (UTF-8) */
138 char szComment[VDI_IMAGE_COMMENT_SIZE];
139 /** Offset of Blocks array from the begining of image file.
140 * Should be sector-aligned for HDD access optimization. */
141 uint32_t offBlocks;
142 /** Offset of image data from the begining of image file.
143 * Should be sector-aligned for HDD access optimization. */
144 uint32_t offData;
145 /** Image geometry. */
146 VDIDISKGEOMETRY Geometry;
147 /** BIOS HDD translation mode, see PDMBIOSTRANSLATION. */
148 uint32_t u32Translation;
149 /** Size of disk (in bytes). */
150 uint64_t cbDisk;
151 /** Block size. (For instance VDI_IMAGE_BLOCK_SIZE.) Should be a power of 2! */
152 uint32_t cbBlock;
153 /** Size of additional service information of every data block.
154 * Prepended before block data. May be 0.
155 * Should be a power of 2 and sector-aligned for optimization reasons. */
156 uint32_t cbBlockExtra;
157 /** Number of blocks. */
158 uint32_t cBlocks;
159 /** Number of allocated blocks. */
160 uint32_t cBlocksAllocated;
161 /** UUID of image. */
162 RTUUID uuidCreate;
163 /** UUID of image's last modification. */
164 RTUUID uuidModify;
165 /** Only for secondary images - UUID of previous image. */
166 RTUUID uuidLinkage;
167 /** Only for secondary images - UUID of previous image's last modification. */
168 RTUUID uuidParentModify;
169} VDIHEADER1, *PVDIHEADER1;
170#pragma pack()
171
172/**
173 * Header structure for all versions.
174 */
175typedef struct VDIHEADER
176{
177 unsigned uVersion;
178 union
179 {
180 VDIHEADER0 v0;
181 VDIHEADER1 v1;
182 } u;
183} VDIHEADER, *PVDIHEADER;
184
185/** Block 'pointer'. */
186typedef uint32_t VDIIMAGEBLOCKPOINTER;
187/** Pointer to a block 'pointer'. */
188typedef VDIIMAGEBLOCKPOINTER *PVDIIMAGEBLOCKPOINTER;
189
190/**
191 * Block marked as free is not allocated in image file, read from this
192 * block may returns any random data.
193 */
194#define VDI_IMAGE_BLOCK_FREE ((VDIIMAGEBLOCKPOINTER)~0)
195
196/**
197 * Block marked as zero is not allocated in image file, read from this
198 * block returns zeroes.
199 */
200#define VDI_IMAGE_BLOCK_ZERO ((VDIIMAGEBLOCKPOINTER)~1)
201
202/**
203 * Block 'pointer' >= VDI_IMAGE_BLOCK_UNALLOCATED indicates block is not
204 * allocated in image file.
205 */
206#define VDI_IMAGE_BLOCK_UNALLOCATED (VDI_IMAGE_BLOCK_ZERO)
207#define IS_VDI_IMAGE_BLOCK_ALLOCATED(bp) (bp < VDI_IMAGE_BLOCK_UNALLOCATED)
208
209#define GET_MAJOR_HEADER_VERSION(ph) (VDI_GET_VERSION_MAJOR((ph)->uVersion))
210#define GET_MINOR_HEADER_VERSION(ph) (VDI_GET_VERSION_MINOR((ph)->uVersion))
211
212/*******************************************************************************
213* Internal Functions for header access *
214*******************************************************************************/
215static VDIIMAGETYPE getImageType(PVDIHEADER ph)
216{
217 switch (GET_MAJOR_HEADER_VERSION(ph))
218 {
219 case 0: return (VDIIMAGETYPE)ph->u.v0.u32Type;
220 case 1: return (VDIIMAGETYPE)ph->u.v1.u32Type;
221 }
222 AssertFailed();
223 return (VDIIMAGETYPE)0;
224}
225
226static unsigned getImageFlags(PVDIHEADER ph)
227{
228 switch (GET_MAJOR_HEADER_VERSION(ph))
229 {
230 case 0: return ph->u.v0.fFlags;
231 case 1: return ph->u.v1.fFlags;
232 }
233 AssertFailed();
234 return 0;
235}
236
237static char *getImageComment(PVDIHEADER ph)
238{
239 switch (GET_MAJOR_HEADER_VERSION(ph))
240 {
241 case 0: return &ph->u.v0.szComment[0];
242 case 1: return &ph->u.v1.szComment[0];
243 }
244 AssertFailed();
245 return NULL;
246}
247
248static unsigned getImageBlocksOffset(PVDIHEADER ph)
249{
250 switch (GET_MAJOR_HEADER_VERSION(ph))
251 {
252 case 0: return (sizeof(VDIPREHEADER) + sizeof(VDIHEADER0));
253 case 1: return ph->u.v1.offBlocks;
254 }
255 AssertFailed();
256 return 0;
257}
258
259static unsigned getImageDataOffset(PVDIHEADER ph)
260{
261 switch (GET_MAJOR_HEADER_VERSION(ph))
262 {
263 case 0: return sizeof(VDIPREHEADER) + sizeof(VDIHEADER0) + \
264 (ph->u.v0.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER));
265 case 1: return ph->u.v1.offData;
266 }
267 AssertFailed();
268 return 0;
269}
270
271static PVDIDISKGEOMETRY getImageGeometry(PVDIHEADER ph)
272{
273 switch (GET_MAJOR_HEADER_VERSION(ph))
274 {
275 case 0: return &ph->u.v0.Geometry;
276 case 1: return &ph->u.v1.Geometry;
277 }
278 AssertFailed();
279 return NULL;
280}
281
282static PDMBIOSTRANSLATION getImageTranslation(PVDIHEADER ph)
283{
284 switch (GET_MAJOR_HEADER_VERSION(ph))
285 {
286 case 0: return PDMBIOSTRANSLATION_AUTO;
287 case 1: return (PDMBIOSTRANSLATION)ph->u.v1.u32Translation;
288 }
289 AssertFailed();
290 return PDMBIOSTRANSLATION_NONE;
291}
292
293static void setImageTranslation(PVDIHEADER ph, PDMBIOSTRANSLATION enmTranslation)
294{
295 switch (GET_MAJOR_HEADER_VERSION(ph))
296 {
297 case 0:
298 return;
299 case 1:
300 ph->u.v1.u32Translation = (uint32_t)enmTranslation;
301 return;
302 }
303 AssertFailed();
304}
305
306static uint64_t getImageDiskSize(PVDIHEADER ph)
307{
308 switch (GET_MAJOR_HEADER_VERSION(ph))
309 {
310 case 0: return ph->u.v0.cbDisk;
311 case 1: return ph->u.v1.cbDisk;
312 }
313 AssertFailed();
314 return 0;
315}
316
317static unsigned getImageBlockSize(PVDIHEADER ph)
318{
319 switch (GET_MAJOR_HEADER_VERSION(ph))
320 {
321 case 0: return ph->u.v0.cbBlock;
322 case 1: return ph->u.v1.cbBlock;
323 }
324 AssertFailed();
325 return 0;
326}
327
328static unsigned getImageExtraBlockSize(PVDIHEADER ph)
329{
330 switch (GET_MAJOR_HEADER_VERSION(ph))
331 {
332 case 0: return 0;
333 case 1: return ph->u.v1.cbBlockExtra;
334 }
335 AssertFailed();
336 return 0;
337}
338
339static unsigned getImageBlocks(PVDIHEADER ph)
340{
341 switch (GET_MAJOR_HEADER_VERSION(ph))
342 {
343 case 0: return ph->u.v0.cBlocks;
344 case 1: return ph->u.v1.cBlocks;
345 }
346 AssertFailed();
347 return 0;
348}
349
350static unsigned getImageBlocksAllocated(PVDIHEADER ph)
351{
352 switch (GET_MAJOR_HEADER_VERSION(ph))
353 {
354 case 0: return ph->u.v0.cBlocksAllocated;
355 case 1: return ph->u.v1.cBlocksAllocated;
356 }
357 AssertFailed();
358 return 0;
359}
360
361static void setImageBlocksAllocated(PVDIHEADER ph, unsigned cBlocks)
362{
363 switch (GET_MAJOR_HEADER_VERSION(ph))
364 {
365 case 0:
366 ph->u.v0.cBlocksAllocated = cBlocks;
367 return;
368 case 1:
369 ph->u.v1.cBlocksAllocated = cBlocks;
370 return;
371 }
372 AssertFailed();
373}
374
375static PRTUUID getImageCreationUUID(PVDIHEADER ph)
376{
377 switch (GET_MAJOR_HEADER_VERSION(ph))
378 {
379 case 0: return &ph->u.v0.uuidCreate;
380 case 1: return &ph->u.v1.uuidCreate;
381 }
382 AssertFailed();
383 return NULL;
384}
385
386static PRTUUID getImageModificationUUID(PVDIHEADER ph)
387{
388 switch (GET_MAJOR_HEADER_VERSION(ph))
389 {
390 case 0: return &ph->u.v0.uuidModify;
391 case 1: return &ph->u.v1.uuidModify;
392 }
393 AssertFailed();
394 return NULL;
395}
396
397static PRTUUID getImageParentUUID(PVDIHEADER ph)
398{
399 switch (GET_MAJOR_HEADER_VERSION(ph))
400 {
401 case 0: return &ph->u.v0.uuidLinkage;
402 case 1: return &ph->u.v1.uuidLinkage;
403 }
404 AssertFailed();
405 return NULL;
406}
407
408static PRTUUID getImageParentModificationUUID(PVDIHEADER ph)
409{
410 switch (GET_MAJOR_HEADER_VERSION(ph))
411 {
412 case 1: return &ph->u.v1.uuidParentModify;
413 }
414 AssertFailed();
415 return NULL;
416}
417
418/**
419 * Default image block size, may be changed by setBlockSize/getBlockSize.
420 *
421 * Note: for speed reasons block size should be a power of 2 !
422 */
423#define VDI_IMAGE_DEFAULT_BLOCK_SIZE (1 * 1024 * 1024)
424
425/**
426 * fModified bit flags.
427 */
428#define VDI_IMAGE_MODIFIED_FLAG (0x01)
429#define VDI_IMAGE_MODIFIED_FIRST (0x02)
430#define VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE (0x04)
431
432/**
433 * Image structure
434 */
435typedef struct VDIIMAGEDESC
436{
437 /** Link to parent image descriptor, if any. */
438 struct VDIIMAGEDESC *pPrev;
439 /** Link to child image descriptor, if any. */
440 struct VDIIMAGEDESC *pNext;
441 /** File handle. */
442 RTFILE File;
443 /** True if the image is operating in readonly mode. */
444 bool fReadOnly;
445 /** Image open flags, VDI_OPEN_FLAGS_*. */
446 unsigned fOpen;
447 /** Image pre-header. */
448 VDIPREHEADER PreHeader;
449 /** Image header. */
450 VDIHEADER Header;
451 /** Pointer to a block array. */
452 PVDIIMAGEBLOCKPOINTER paBlocks;
453 /** fFlags copy from image header, for speed optimization. */
454 unsigned fFlags;
455 /** Start offset of block array in image file, here for speed optimization. */
456 unsigned offStartBlocks;
457 /** Start offset of data in image file, here for speed optimization. */
458 unsigned offStartData;
459 /** Block mask for getting the offset into a block from a byte hdd offset. */
460 unsigned uBlockMask;
461 /** Block shift value for converting byte hdd offset into paBlock index. */
462 unsigned uShiftOffset2Index;
463 /** Block shift value for converting block index into offset in image. */
464 unsigned uShiftIndex2Offset;
465 /** Offset of data from the beginning of block. */
466 unsigned offStartBlockData;
467 /** Image is modified flags (VDI_IMAGE_MODIFIED*). */
468 unsigned fModified;
469 /** Container filename. (UTF-8)
470 * @todo Make this variable length to save a bunch of bytes. (low prio) */
471 char szFilename[RTPATH_MAX];
472} VDIIMAGEDESC, *PVDIIMAGEDESC;
473
474/**
475 * Default work buffer size, may be changed by setBufferSize() method.
476 *
477 * For best speed performance it must be equal to image block size.
478 */
479#define VDIDISK_DEFAULT_BUFFER_SIZE (VDI_IMAGE_DEFAULT_BLOCK_SIZE)
480
481/** VDIDISK Signature. */
482#define VDIDISK_SIGNATURE (0xbedafeda)
483
484/**
485 * VBox HDD Container main structure, private part.
486 */
487struct VDIDISK
488{
489 /** Structure signature (VDIDISK_SIGNATURE). */
490 uint32_t u32Signature;
491
492 /** Number of opened images. */
493 unsigned cImages;
494
495 /** Base image. */
496 PVDIIMAGEDESC pBase;
497
498 /** Last opened image in the chain.
499 * The same as pBase if only one image is used or the last opened diff image. */
500 PVDIIMAGEDESC pLast;
501
502 /** Default block size for newly created images. */
503 unsigned cbBlock;
504
505 /** Working buffer size, allocated only while committing data,
506 * copying block from primary image to secondary and saving previously
507 * zero block. Buffer deallocated after operation complete.
508 * @remark For best performance buffer size must be equal to image's
509 * block size, however it may be decreased for memory saving.
510 */
511 unsigned cbBuf;
512
513 /** The media interface. */
514 PDMIMEDIA IMedia;
515 /** Pointer to the driver instance. */
516 PPDMDRVINS pDrvIns;
517};
518
519
520/** Converts a pointer to VDIDISK::IMedia to a PVDIDISK. */
521#define PDMIMEDIA_2_VDIDISK(pInterface) ( (PVDIDISK)((uintptr_t)pInterface - RT_OFFSETOF(VDIDISK, IMedia)) )
522
523/** Converts a pointer to PDMDRVINS::IBase to a PPDMDRVINS. */
524#define PDMIBASE_2_DRVINS(pInterface) ( (PPDMDRVINS)((uintptr_t)pInterface - RT_OFFSETOF(PDMDRVINS, IBase)) )
525
526/** Converts a pointer to PDMDRVINS::IBase to a PVDIDISK. */
527#define PDMIBASE_2_VDIDISK(pInterface) ( PDMINS2DATA(PDMIBASE_2_DRVINS(pInterface), PVDIDISK) )
528
529
530/*******************************************************************************
531* Internal Functions *
532*******************************************************************************/
533static unsigned getPowerOfTwo(unsigned uNumber);
534static void vdiInitPreHeader(PVDIPREHEADER pPreHdr);
535static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr);
536static void vdiInitHeader(PVDIHEADER pHeader,
537 VDIIMAGETYPE enmType,
538 uint32_t fFlags,
539 const char *pszComment,
540 uint64_t cbDisk,
541 uint32_t cbBlock,
542 uint32_t cbBlockExtra);
543static int vdiValidateHeader(PVDIHEADER pHeader);
544static int vdiCreateImage(const char *pszFilename,
545 VDIIMAGETYPE enmType,
546 unsigned fFlags,
547 uint64_t cbSize,
548 const char *pszComment,
549 PVDIIMAGEDESC pParent,
550 PFNVMPROGRESS pfnProgress, void *pvUser);
551static void vdiInitImageDesc(PVDIIMAGEDESC pImage);
552static void vdiSetupImageDesc(PVDIIMAGEDESC pImage);
553static int vdiOpenImage(PVDIIMAGEDESC *ppImage,
554 const char *pszFilename,
555 unsigned fOpen,
556 PVDIIMAGEDESC pParent);
557static int vdiUpdateHeader(PVDIIMAGEDESC pImage);
558static int vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock);
559static int vdiUpdateBlocks(PVDIIMAGEDESC pImage);
560static void vdiSetModifiedFlag(PVDIIMAGEDESC pImage);
561static void vdiResetModifiedFlag(PVDIIMAGEDESC pImage);
562static void vdiDisableLastModifiedUpdate(PVDIIMAGEDESC pImage);
563#if 0 /* unused */
564static void vdiEnableLastModifiedUpdate(PVDIIMAGEDESC pImage);
565#endif
566static void vdiFlushImage(PVDIIMAGEDESC pImage);
567static void vdiCloseImage(PVDIIMAGEDESC pImage);
568static int vdiReadInBlock(PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offRead, unsigned cbToRead, void *pvBuf);
569static int vdiFillBlockByZeroes(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock);
570static int vdiWriteInBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offWrite, unsigned cbToWrite, const void *pvBuf);
571static int vdiCopyBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock);
572static int vdiMergeImages(PVDIIMAGEDESC pImageFrom, PVDIIMAGEDESC pImageTo, bool fParentToChild,
573 PFNVMPROGRESS pfnProgress, void *pvUser);
574static int vdiCommitToImage(PVDIDISK pDisk, PVDIIMAGEDESC pDstImage,
575 PFNVMPROGRESS pfnProgress, void *pvUser);
576static void vdiInitVDIDisk(PVDIDISK pDisk);
577static void vdiAddImageToList(PVDIDISK pDisk, PVDIIMAGEDESC pImage);
578static void vdiRemoveImageFromList(PVDIDISK pDisk, PVDIIMAGEDESC pImage);
579static PVDIIMAGEDESC vdiGetImageByNumber(PVDIDISK pDisk, int nImage);
580static int vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly);
581static int vdiUpdateReadOnlyHeader(PVDIIMAGEDESC pImage);
582static void vdiDumpImage(PVDIIMAGEDESC pImage);
583
584static DECLCALLBACK(int) vdiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle);
585static DECLCALLBACK(void) vdiDestruct(PPDMDRVINS pDrvIns);
586static DECLCALLBACK(int) vdiRead(PPDMIMEDIA pInterface, uint64_t off, void *pvBuf, size_t cbRead);
587static DECLCALLBACK(int) vdiWrite(PPDMIMEDIA pInterface, uint64_t off, const void *pvBuf, size_t cbWrite);
588static DECLCALLBACK(int) vdiFlush(PPDMIMEDIA pInterface);
589static DECLCALLBACK(uint64_t) vdiGetSize(PPDMIMEDIA pInterface);
590static DECLCALLBACK(int) vdiBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders, uint32_t *pcHeads, uint32_t *pcSectors);
591static DECLCALLBACK(int) vdiBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders, uint32_t cHeads, uint32_t cSectors);
592static DECLCALLBACK(int) vdiGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid);
593static DECLCALLBACK(bool) vdiIsReadOnly(PPDMIMEDIA pInterface);
594static DECLCALLBACK(int) vdiBiosGetTranslation(PPDMIMEDIA pInterface, PPDMBIOSTRANSLATION penmTranslation);
595static DECLCALLBACK(int) vdiBiosSetTranslation(PPDMIMEDIA pInterface, PDMBIOSTRANSLATION enmTranslation);
596static DECLCALLBACK(void *) vdiQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface);
597
598
599/**
600 * internal: return power of 2 or 0 if num error.
601 */
602static unsigned getPowerOfTwo(unsigned uNumber)
603{
604 if (uNumber == 0)
605 return 0;
606 unsigned uPower2 = 0;
607 while ((uNumber & 1) == 0)
608 {
609 uNumber >>= 1;
610 uPower2++;
611 }
612 return uNumber == 1 ? uPower2 : 0;
613}
614
615/**
616 * internal: init HDD preheader.
617 */
618static void vdiInitPreHeader(PVDIPREHEADER pPreHdr)
619{
620 pPreHdr->u32Signature = VDI_IMAGE_SIGNATURE;
621 pPreHdr->u32Version = VDI_IMAGE_VERSION;
622 memset(pPreHdr->szFileInfo, 0, sizeof(pPreHdr->szFileInfo));
623 strncat(pPreHdr->szFileInfo, VDI_IMAGE_FILE_INFO, sizeof(pPreHdr->szFileInfo));
624}
625
626/**
627 * internal: check HDD preheader.
628 */
629static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr)
630{
631 if (pPreHdr->u32Signature != VDI_IMAGE_SIGNATURE)
632 return VERR_VDI_INVALID_SIGNATURE;
633
634 if ( pPreHdr->u32Version != VDI_IMAGE_VERSION
635 && pPreHdr->u32Version != 0x00000002) /* old version. */
636 return VERR_VDI_UNSUPPORTED_VERSION;
637
638 return VINF_SUCCESS;
639}
640
641/**
642 * internal: init HDD header. Always use latest header version.
643 * @param pHeader Assumes it was initially initialized to all zeros.
644 */
645static void vdiInitHeader(PVDIHEADER pHeader,
646 VDIIMAGETYPE enmType,
647 uint32_t fFlags,
648 const char *pszComment,
649 uint64_t cbDisk,
650 uint32_t cbBlock,
651 uint32_t cbBlockExtra)
652{
653 pHeader->uVersion = VDI_IMAGE_VERSION;
654 pHeader->u.v1.cbHeader = sizeof(VDIHEADER1);
655 pHeader->u.v1.u32Type = (uint32_t)enmType;
656 pHeader->u.v1.fFlags = fFlags;
657#ifdef VBOX_STRICT
658 char achZero[VDI_IMAGE_COMMENT_SIZE] = {0};
659 Assert(!memcmp(pHeader->u.v1.szComment, achZero, VDI_IMAGE_COMMENT_SIZE));
660#endif
661 pHeader->u.v1.szComment[0] = '\0';
662 if (pszComment)
663 {
664 AssertMsg(strlen(pszComment) < sizeof(pHeader->u.v1.szComment),
665 ("HDD Comment is too long, cb=%d\n", strlen(pszComment)));
666 strncat(pHeader->u.v1.szComment, pszComment, sizeof(pHeader->u.v1.szComment));
667 }
668
669 /* Mark the geometry not-calculated. */
670 pHeader->u.v1.Geometry.cCylinders = 0;
671 pHeader->u.v1.Geometry.cHeads = 0;
672 pHeader->u.v1.Geometry.cSectors = 0;
673 pHeader->u.v1.Geometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
674 pHeader->u.v1.u32Translation = PDMBIOSTRANSLATION_AUTO;
675
676 pHeader->u.v1.cbDisk = cbDisk;
677 pHeader->u.v1.cbBlock = cbBlock;
678 pHeader->u.v1.cBlocks = (uint32_t)(cbDisk / cbBlock);
679 if (cbDisk % cbBlock)
680 pHeader->u.v1.cBlocks++;
681 pHeader->u.v1.cbBlockExtra = cbBlockExtra;
682 pHeader->u.v1.cBlocksAllocated = 0;
683
684 /* Init offsets. */
685 pHeader->u.v1.offBlocks = RT_ALIGN_32(sizeof(VDIPREHEADER) + sizeof(VDIHEADER1), VDI_GEOMETRY_SECTOR_SIZE);
686 pHeader->u.v1.offData = RT_ALIGN_32(pHeader->u.v1.offBlocks + (pHeader->u.v1.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER)), VDI_GEOMETRY_SECTOR_SIZE);
687
688 /* Init uuids. */
689 RTUuidCreate(&pHeader->u.v1.uuidCreate);
690 RTUuidClear(&pHeader->u.v1.uuidModify);
691 RTUuidClear(&pHeader->u.v1.uuidLinkage);
692 RTUuidClear(&pHeader->u.v1.uuidParentModify);
693}
694
695/**
696 * internal: check HDD header.
697 */
698static int vdiValidateHeader(PVDIHEADER pHeader)
699{
700 /* Check verion-dependend header parameters. */
701 switch (GET_MAJOR_HEADER_VERSION(pHeader))
702 {
703 case 0:
704 {
705 /* Old header version. */
706 break;
707 }
708 case 1:
709 {
710 /* Current header version. */
711
712 if (pHeader->u.v1.cbHeader < sizeof(VDIHEADER1))
713 return VERR_VDI_INVALID_HEADER;
714
715 if (getImageBlocksOffset(pHeader) < (sizeof(VDIPREHEADER) + sizeof(VDIHEADER1)))
716 return VERR_VDI_INVALID_HEADER;
717
718 if (getImageDataOffset(pHeader) < (getImageBlocksOffset(pHeader) + getImageBlocks(pHeader) * sizeof(VDIIMAGEBLOCKPOINTER)))
719 return VERR_VDI_INVALID_HEADER;
720
721 if ( getImageType(pHeader) == VDI_IMAGE_TYPE_UNDO
722 || getImageType(pHeader) == VDI_IMAGE_TYPE_DIFF)
723 {
724 if (RTUuidIsNull(getImageParentUUID(pHeader)))
725 return VERR_VDI_INVALID_HEADER;
726 if (RTUuidIsNull(getImageParentModificationUUID(pHeader)))
727 return VERR_VDI_INVALID_HEADER;
728 }
729
730 break;
731 }
732 default:
733 /* Unsupported. */
734 return VERR_VDI_UNSUPPORTED_VERSION;
735 }
736
737 /* Check common header parameters. */
738
739 if ( getImageType(pHeader) < VDI_IMAGE_TYPE_FIRST
740 || getImageType(pHeader) > VDI_IMAGE_TYPE_LAST)
741 return VERR_VDI_INVALID_TYPE;
742
743 if (getImageFlags(pHeader) & ~VDI_IMAGE_FLAGS_MASK)
744 return VERR_VDI_INVALID_FLAGS;
745
746 if ((getImageGeometry(pHeader))->cbSector != VDI_GEOMETRY_SECTOR_SIZE)
747 return VERR_VDI_INVALID_HEADER;
748
749 if ( getImageDiskSize(pHeader) == 0
750 || getImageBlockSize(pHeader) == 0
751 || getImageBlocks(pHeader) == 0
752 || getPowerOfTwo(getImageBlockSize(pHeader)) == 0
753 || getImageBlocksAllocated(pHeader) > getImageBlocks(pHeader))
754 return VERR_VDI_INVALID_HEADER;
755
756 if ( getImageExtraBlockSize(pHeader) != 0
757 && getPowerOfTwo(getImageExtraBlockSize(pHeader)) == 0)
758 return VERR_VDI_INVALID_HEADER;
759
760 if ( (uint64_t)getImageBlockSize(pHeader) * getImageBlocks(pHeader) < getImageDiskSize(pHeader))
761 return VERR_VDI_INVALID_HEADER;
762
763 if (RTUuidIsNull(getImageCreationUUID(pHeader)))
764 return VERR_VDI_INVALID_HEADER;
765 if (RTUuidIsNull(getImageModificationUUID(pHeader)))
766 return VERR_VDI_INVALID_HEADER;
767
768 return VINF_SUCCESS;
769}
770
771/**
772 * internal: init VDIIMAGEDESC structure.
773 */
774static void vdiInitImageDesc(PVDIIMAGEDESC pImage)
775{
776 pImage->pPrev = NULL;
777 pImage->pNext = NULL;
778 pImage->File = NIL_RTFILE;
779 pImage->paBlocks = NULL;
780}
781
782/**
783 * internal: setup VDIIMAGEDESC structure by image header.
784 */
785static void vdiSetupImageDesc(PVDIIMAGEDESC pImage)
786{
787 pImage->fFlags = getImageFlags(&pImage->Header);
788 pImage->offStartBlocks = getImageBlocksOffset(&pImage->Header);
789 pImage->offStartData = getImageDataOffset(&pImage->Header);
790 pImage->uBlockMask = getImageBlockSize(&pImage->Header) - 1;
791 pImage->uShiftIndex2Offset = pImage->uShiftOffset2Index = getPowerOfTwo(getImageBlockSize(&pImage->Header));
792 pImage->offStartBlockData = getImageExtraBlockSize(&pImage->Header);
793 if (pImage->offStartBlockData != 0)
794 pImage->uShiftIndex2Offset += getPowerOfTwo(pImage->offStartBlockData);
795}
796
797/**
798 * internal: create image.
799 */
800static int vdiCreateImage(const char *pszFilename,
801 VDIIMAGETYPE enmType,
802 unsigned fFlags,
803 uint64_t cbSize,
804 const char *pszComment,
805 PVDIIMAGEDESC pParent,
806 PFNVMPROGRESS pfnProgress, void *pvUser)
807{
808 /* Check args. */
809 Assert(pszFilename);
810 Assert(enmType >= VDI_IMAGE_TYPE_FIRST && enmType <= VDI_IMAGE_TYPE_LAST);
811 Assert(!(fFlags & ~VDI_IMAGE_FLAGS_MASK));
812 Assert(cbSize);
813
814 /* Special check for comment length. */
815 if ( pszComment
816 && strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
817 {
818 Log(("vdiCreateImage: pszComment is too long, cb=%d\n", strlen(pszComment)));
819 return VERR_VDI_COMMENT_TOO_LONG;
820 }
821
822 if ( enmType == VDI_IMAGE_TYPE_UNDO
823 || enmType == VDI_IMAGE_TYPE_DIFF)
824 {
825 Assert(pParent);
826 if ((pParent->PreHeader.u32Version >> 16) != VDI_IMAGE_VERSION_MAJOR)
827 {
828 /* Invalid parent image version. */
829 Log(("vdiCreateImage: unsupported parent version=%08X\n", pParent->PreHeader.u32Version));
830 return VERR_VDI_UNSUPPORTED_VERSION;
831 }
832
833 /* get image params from the parent image. */
834 fFlags = getImageFlags(&pParent->Header);
835 cbSize = getImageDiskSize(&pParent->Header);
836 }
837
838 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
839 if (!pImage)
840 return VERR_NO_MEMORY;
841 vdiInitImageDesc(pImage);
842
843 vdiInitPreHeader(&pImage->PreHeader);
844 vdiInitHeader(&pImage->Header, enmType, fFlags, pszComment, cbSize, VDI_IMAGE_DEFAULT_BLOCK_SIZE, 0);
845
846 if ( enmType == VDI_IMAGE_TYPE_UNDO
847 || enmType == VDI_IMAGE_TYPE_DIFF)
848 {
849 /* Set up linkage information. */
850 pImage->Header.u.v1.uuidLinkage = *getImageCreationUUID(&pParent->Header);
851 pImage->Header.u.v1.uuidParentModify = *getImageModificationUUID(&pParent->Header);
852 }
853
854 pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
855 if (!pImage->paBlocks)
856 {
857 RTMemFree(pImage);
858 return VERR_NO_MEMORY;
859 }
860
861 if (enmType != VDI_IMAGE_TYPE_FIXED)
862 {
863 /* for growing images mark all blocks in paBlocks as free. */
864 for (unsigned i = 0; i < pImage->Header.u.v1.cBlocks; i++)
865 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
866 }
867 else
868 {
869 /* for fixed images mark all blocks in paBlocks as allocated */
870 for (unsigned i = 0; i < pImage->Header.u.v1.cBlocks; i++)
871 pImage->paBlocks[i] = i;
872 pImage->Header.u.v1.cBlocksAllocated = pImage->Header.u.v1.cBlocks;
873 }
874
875 /* Setup image parameters. */
876 vdiSetupImageDesc(pImage);
877
878 /* create file */
879 int rc = RTFileOpen(&pImage->File,
880 pszFilename,
881 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
882 if (VBOX_SUCCESS(rc))
883 {
884 /* Lock image exclusively to close any wrong access by VDI API calls. */
885 uint64_t cbLock = pImage->offStartData
886 + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
887 rc = RTFileLock(pImage->File,
888 RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
889 if (VBOX_FAILURE(rc))
890 {
891 cbLock = 0; /* Not locked. */
892 goto l_create_failed;
893 }
894
895 if (enmType == VDI_IMAGE_TYPE_FIXED)
896 {
897 /*
898 * Allocate & commit whole file if fixed image, it must be more
899 * effective than expanding file by write operations.
900 */
901 rc = RTFileSetSize(pImage->File,
902 pImage->offStartData
903 + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset));
904 }
905 else
906 {
907 /* Set file size to hold header and blocks array. */
908 rc = RTFileSetSize(pImage->File, pImage->offStartData);
909 }
910 if (VBOX_FAILURE(rc))
911 goto l_create_failed;
912
913 /* Generate image last-modify uuid */
914 RTUuidCreate(getImageModificationUUID(&pImage->Header));
915
916 /* Write pre-header. */
917 rc = RTFileWrite(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
918 if (VBOX_FAILURE(rc))
919 goto l_create_failed;
920
921 /* Write header. */
922 rc = RTFileWrite(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
923 if (VBOX_FAILURE(rc))
924 goto l_create_failed;
925
926 /* Write blocks array. */
927 rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
928 if (VBOX_FAILURE(rc))
929 goto l_create_failed;
930 rc = RTFileWrite(pImage->File,
931 pImage->paBlocks,
932 getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
933 NULL);
934 if (VBOX_FAILURE(rc))
935 goto l_create_failed;
936
937 if ( (enmType == VDI_IMAGE_TYPE_FIXED)
938 && (fFlags & VDI_IMAGE_FLAGS_ZERO_EXPAND))
939 {
940 /* Fill image with zeroes. */
941
942 rc = RTFileSeek(pImage->File, pImage->offStartData, RTFILE_SEEK_BEGIN, NULL);
943 if (VBOX_FAILURE(rc))
944 goto l_create_failed;
945
946 /* alloc tmp zero-filled buffer */
947 void *pvBuf = RTMemTmpAllocZ(VDIDISK_DEFAULT_BUFFER_SIZE);
948 if (pvBuf)
949 {
950 uint64_t cbFill = (uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset;
951 uint64_t cbDisk = cbFill;
952
953 /* do loop to fill all image. */
954 while (cbFill > 0)
955 {
956 unsigned to_fill = (unsigned)RT_MIN(cbFill, VDIDISK_DEFAULT_BUFFER_SIZE);
957
958 rc = RTFileWrite(pImage->File, pvBuf, to_fill, NULL);
959 if (VBOX_FAILURE(rc))
960 break;
961
962 cbFill -= to_fill;
963
964 if (pfnProgress)
965 {
966 rc = pfnProgress(NULL /* Achtung! pVM=NULL */,
967 (unsigned)(((cbDisk - cbFill) * 100) / cbDisk),
968 pvUser);
969 if (VBOX_FAILURE(rc))
970 break;
971 }
972 }
973 RTMemTmpFree(pvBuf);
974 }
975 else
976 {
977 /* alloc error */
978 rc = VERR_NO_MEMORY;
979 }
980 }
981
982 l_create_failed:
983
984 if (cbLock)
985 RTFileUnlock(pImage->File, 0, cbLock);
986
987 RTFileClose(pImage->File);
988
989 /* Delete image file if error occured while creating */
990 if (VBOX_FAILURE(rc))
991 RTFileDelete(pszFilename);
992 }
993
994 RTMemFree(pImage->paBlocks);
995 RTMemFree(pImage);
996
997 if ( VBOX_SUCCESS(rc)
998 && pfnProgress)
999 pfnProgress(NULL /* Achtung! pVM=NULL */, 100, pvUser);
1000
1001 Log(("vdiCreateImage: done, filename=\"%s\", rc=%Vrc\n", pszFilename, rc));
1002
1003 return rc;
1004}
1005
1006/**
1007 * Open an image.
1008 * @internal
1009 */
1010static int vdiOpenImage(PVDIIMAGEDESC *ppImage,
1011 const char *pszFilename,
1012 unsigned fOpen,
1013 PVDIIMAGEDESC pParent)
1014{
1015 /*
1016 * Validate input.
1017 */
1018 Assert(ppImage);
1019 Assert(pszFilename);
1020 Assert(!(fOpen & ~VDI_OPEN_FLAGS_MASK));
1021
1022 PVDIIMAGEDESC pImage;
1023 size_t cchFilename = strlen(pszFilename);
1024 if (cchFilename >= sizeof(pImage->szFilename))
1025 {
1026 AssertMsgFailed(("filename=\"%s\" is too long (%d bytes)!\n", pszFilename, cchFilename));
1027 return VERR_FILENAME_TOO_LONG;
1028 }
1029
1030 pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
1031 if (!pImage)
1032 return VERR_NO_MEMORY;
1033 vdiInitImageDesc(pImage);
1034
1035 memcpy(pImage->szFilename, pszFilename, cchFilename);
1036 pImage->fOpen = fOpen;
1037
1038 /*
1039 * Open the image.
1040 */
1041 int rc = RTFileOpen(&pImage->File,
1042 pImage->szFilename,
1043 fOpen & VDI_OPEN_FLAGS_READONLY
1044 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
1045 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
1046 if (VBOX_FAILURE(rc))
1047 {
1048 if (!(fOpen & VDI_OPEN_FLAGS_READONLY))
1049 {
1050 /* Try to open image for reading only. */
1051 rc = RTFileOpen(&pImage->File,
1052 pImage->szFilename,
1053 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
1054 if (VBOX_SUCCESS(rc))
1055 pImage->fOpen |= VDI_OPEN_FLAGS_READONLY;
1056 }
1057 if (VBOX_FAILURE(rc))
1058 {
1059 RTMemFree(pImage);
1060 return rc;
1061 }
1062 }
1063 /* Set up current image r/w state. */
1064 pImage->fReadOnly = !!(pImage->fOpen & VDI_OPEN_FLAGS_READONLY);
1065
1066 /*
1067 * Set initial file lock for reading header only.
1068 * Length of lock doesn't matter, it just must include image header.
1069 */
1070 uint64_t cbLock = _1M;
1071 rc = RTFileLock(pImage->File, RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
1072 if (VBOX_FAILURE(rc))
1073 {
1074 cbLock = 0;
1075 goto l_open_failed;
1076 }
1077
1078 /* Read pre-header. */
1079 rc = RTFileRead(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
1080 if (VBOX_FAILURE(rc))
1081 goto l_open_failed;
1082 rc = vdiValidatePreHeader(&pImage->PreHeader);
1083 if (VBOX_FAILURE(rc))
1084 goto l_open_failed;
1085
1086 /* Read header. */
1087 pImage->Header.uVersion = pImage->PreHeader.u32Version;
1088 switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
1089 {
1090 case 0:
1091 rc = RTFileRead(pImage->File, &pImage->Header.u.v0, sizeof(pImage->Header.u.v0), NULL);
1092 break;
1093 case 1:
1094 rc = RTFileRead(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
1095 break;
1096 default:
1097 rc = VERR_VDI_UNSUPPORTED_VERSION;
1098 break;
1099 }
1100 if (VBOX_FAILURE(rc))
1101 goto l_open_failed;
1102 rc = vdiValidateHeader(&pImage->Header);
1103 if (VBOX_FAILURE(rc))
1104 goto l_open_failed;
1105
1106 /* Check diff image correctness. */
1107 if (pParent)
1108 {
1109 if (pImage->PreHeader.u32Version != pParent->PreHeader.u32Version)
1110 {
1111 rc = VERR_VDI_IMAGES_VERSION_MISMATCH;
1112 goto l_open_failed;
1113 }
1114
1115 if ( getImageType(&pImage->Header) != VDI_IMAGE_TYPE_UNDO
1116 && getImageType(&pImage->Header) != VDI_IMAGE_TYPE_DIFF)
1117 {
1118 rc = VERR_VDI_WRONG_DIFF_IMAGE;
1119 goto l_open_failed;
1120 }
1121
1122 if ( getImageDiskSize(&pImage->Header) != getImageDiskSize(&pParent->Header)
1123 || getImageBlockSize(&pImage->Header) != getImageBlockSize(&pParent->Header)
1124 || getImageBlocks(&pImage->Header) != getImageBlocks(&pParent->Header)
1125 || getImageExtraBlockSize(&pImage->Header) != getImageExtraBlockSize(&pParent->Header))
1126 {
1127 rc = VERR_VDI_WRONG_DIFF_IMAGE;
1128 goto l_open_failed;
1129 }
1130
1131 /* Check linkage data. */
1132 if ( RTUuidCompare(getImageParentUUID(&pImage->Header), getImageCreationUUID(&pParent->Header))
1133 || RTUuidCompare(getImageParentModificationUUID(&pImage->Header), getImageModificationUUID(&pParent->Header)))
1134 {
1135 rc = VERR_VDI_IMAGES_UUID_MISMATCH;
1136 goto l_open_failed;
1137 }
1138 }
1139
1140 /* Setup image parameters by header. */
1141 vdiSetupImageDesc(pImage);
1142
1143 /* reset modified flag into first-modified state. */
1144 pImage->fModified = VDI_IMAGE_MODIFIED_FIRST;
1145
1146 /* Image is validated, set working file lock on it. */
1147 rc = RTFileUnlock(pImage->File, 0, cbLock);
1148 AssertRC(rc);
1149 cbLock = pImage->offStartData
1150 + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
1151 rc = RTFileLock(pImage->File,
1152 (pImage->fReadOnly) ?
1153 RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY :
1154 RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY,
1155 0,
1156 cbLock);
1157 if ( VBOX_FAILURE(rc)
1158 && !pImage->fReadOnly)
1159 {
1160 /* Failed to lock image for writing, try read-only lock. */
1161 rc = RTFileLock(pImage->File,
1162 RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
1163 if (VBOX_SUCCESS(rc))
1164 pImage->fReadOnly = true;
1165 }
1166 if (VBOX_FAILURE(rc))
1167 {
1168 cbLock = 0; /* Not locked. */
1169 goto l_open_failed;
1170 }
1171
1172 /* Allocate memory for blocks array. */
1173 pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
1174 if (!pImage->paBlocks)
1175 {
1176 rc = VERR_NO_MEMORY;
1177 goto l_open_failed;
1178 }
1179
1180 /* Read blocks array. */
1181 rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
1182 if (VBOX_FAILURE(rc))
1183 goto l_open_failed;
1184 rc = RTFileRead(pImage->File,
1185 pImage->paBlocks,
1186 getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
1187 NULL);
1188 if (VBOX_FAILURE(rc))
1189 goto l_open_failed;
1190
1191 /* all done. */
1192 *ppImage = pImage;
1193 return VINF_SUCCESS;
1194
1195l_open_failed:
1196 /* Clean up. */
1197 if (pImage->paBlocks)
1198 RTMemFree(pImage->paBlocks);
1199 if (cbLock)
1200 RTFileUnlock(pImage->File, 0, cbLock);
1201 RTFileClose(pImage->File);
1202 RTMemFree(pImage);
1203 Log(("vdiOpenImage: failed, filename=\"%s\", rc=%Vrc\n", pszFilename, rc));
1204 return rc;
1205}
1206
1207/**
1208 * internal: save header to file.
1209 */
1210static int vdiUpdateHeader(PVDIIMAGEDESC pImage)
1211{
1212 /* Seek to header start. */
1213 int rc = RTFileSeek(pImage->File, sizeof(VDIPREHEADER), RTFILE_SEEK_BEGIN, NULL);
1214 if (VBOX_SUCCESS(rc))
1215 {
1216 switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
1217 {
1218 case 0:
1219 rc = RTFileWrite(pImage->File, &pImage->Header.u.v0, sizeof(pImage->Header.u.v0), NULL);
1220 break;
1221 case 1:
1222 rc = RTFileWrite(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
1223 break;
1224 default:
1225 rc = VERR_VDI_UNSUPPORTED_VERSION;
1226 break;
1227 }
1228 }
1229 AssertMsgRC(rc, ("vdiUpdateHeader failed, filename=\"%s\" rc=%Vrc\n", pImage->szFilename, rc));
1230 return rc;
1231}
1232
1233/**
1234 * internal: save block pointer to file, save header to file.
1235 */
1236static int vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock)
1237{
1238 /* Update image header. */
1239 int rc = vdiUpdateHeader(pImage);
1240 if (VBOX_SUCCESS(rc))
1241 {
1242 /* write only one block pointer. */
1243 rc = RTFileSeek(pImage->File,
1244 pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
1245 RTFILE_SEEK_BEGIN,
1246 NULL);
1247 if (VBOX_SUCCESS(rc))
1248 rc = RTFileWrite(pImage->File,
1249 &pImage->paBlocks[uBlock],
1250 sizeof(VDIIMAGEBLOCKPOINTER),
1251 NULL);
1252 AssertMsgRC(rc, ("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%Vrc\n",
1253 uBlock, pImage->szFilename, rc));
1254 }
1255 return rc;
1256}
1257
1258/**
1259 * internal: save blocks array to file, save header to file.
1260 */
1261static int vdiUpdateBlocks(PVDIIMAGEDESC pImage)
1262{
1263 /* Update image header. */
1264 int rc = vdiUpdateHeader(pImage);
1265 if (VBOX_SUCCESS(rc))
1266 {
1267 /* write the block pointers array. */
1268 rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
1269 if (VBOX_SUCCESS(rc))
1270 rc = RTFileWrite(pImage->File,
1271 pImage->paBlocks,
1272 sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header),
1273 NULL);
1274 AssertMsgRC(rc, ("vdiUpdateBlocks failed, filename=\"%s\", rc=%Vrc\n",
1275 pImage->szFilename, rc));
1276 }
1277 return rc;
1278}
1279
1280/**
1281 * internal: mark image as modified, if this is the first change - update image header
1282 * on disk with a new uuidModify value.
1283 */
1284static void vdiSetModifiedFlag(PVDIIMAGEDESC pImage)
1285{
1286 pImage->fModified |= VDI_IMAGE_MODIFIED_FLAG;
1287 if (pImage->fModified & VDI_IMAGE_MODIFIED_FIRST)
1288 {
1289 pImage->fModified &= ~VDI_IMAGE_MODIFIED_FIRST;
1290
1291 /* first modify - generate uuidModify and save to file. */
1292 vdiResetModifiedFlag(pImage);
1293
1294 if (!(pImage->fModified | VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
1295 {
1296 /* save header to file,
1297 * note: no rc checking.
1298 */
1299 vdiUpdateHeader(pImage);
1300 }
1301 }
1302}
1303
1304/**
1305 * internal: generate new uuidModify if the image was changed.
1306 */
1307static void vdiResetModifiedFlag(PVDIIMAGEDESC pImage)
1308{
1309 if (pImage->fModified & VDI_IMAGE_MODIFIED_FLAG)
1310 {
1311 /* generate new last-modified uuid */
1312 if (!(pImage->fModified | VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
1313 RTUuidCreate(getImageModificationUUID(&pImage->Header));
1314
1315 pImage->fModified &= ~VDI_IMAGE_MODIFIED_FLAG;
1316 }
1317}
1318
1319/**
1320 * internal: disables updates of the last-modified UUID
1321 * when performing image writes.
1322 */
1323static void vdiDisableLastModifiedUpdate(PVDIIMAGEDESC pImage)
1324{
1325 pImage->fModified |= VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE;
1326}
1327
1328#if 0 /* unused */
1329/**
1330 * internal: enables updates of the last-modified UUID
1331 * when performing image writes.
1332 */
1333static void vdiEnableLastModifiedUpdate(PVDIIMAGEDESC pImage)
1334{
1335 pImage->fModified &= ~VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE;
1336}
1337#endif
1338
1339/**
1340 * internal: flush image file to disk.
1341 */
1342static void vdiFlushImage(PVDIIMAGEDESC pImage)
1343{
1344 if (!pImage->fReadOnly)
1345 {
1346 /* Update last-modified uuid if need. */
1347 vdiResetModifiedFlag(pImage);
1348
1349 /* Save header. */
1350 int rc = vdiUpdateHeader(pImage);
1351 AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
1352 pImage->szFilename, rc));
1353 RTFileFlush(pImage->File);
1354 }
1355}
1356
1357/**
1358 * internal: close image file.
1359 */
1360static void vdiCloseImage(PVDIIMAGEDESC pImage)
1361{
1362 /* Params checking. */
1363 Assert(pImage);
1364 Assert(pImage->File != NIL_RTFILE);
1365
1366 vdiFlushImage(pImage);
1367 RTFileUnlock(pImage->File,
1368 0,
1369 pImage->offStartData
1370 + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset));
1371 RTFileClose(pImage->File);
1372
1373 /* free image resources */
1374 RTMemFree(pImage->paBlocks);
1375 RTMemFree(pImage);
1376}
1377
1378/**
1379 * internal: read data inside image block.
1380 *
1381 * note: uBlock must be valid, readed data must not overlap block bounds.
1382 */
1383static int vdiReadInBlock(PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offRead, unsigned cbToRead, void *pvBuf)
1384{
1385 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
1386 {
1387 /* block present in image file */
1388 uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
1389 + (pImage->offStartData + pImage->offStartBlockData + offRead);
1390 int rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
1391 if (VBOX_SUCCESS(rc))
1392 rc = RTFileRead(pImage->File, pvBuf, cbToRead, NULL);
1393 if (VBOX_FAILURE(rc))
1394 Log(("vdiReadInBlock: rc=%Vrc filename=\"%s\" uBlock=%u offRead=%u cbToRead=%u u64Offset=%llu\n",
1395 rc, pImage->szFilename, uBlock, offRead, cbToRead, u64Offset));
1396 return rc;
1397 }
1398
1399 /* Returns zeroes for both free and zero block types. */
1400 memset(pvBuf, 0, cbToRead);
1401 return VINF_SUCCESS;
1402}
1403
1404/**
1405 * Read data from virtual HDD.
1406 *
1407 * @returns VBox status code.
1408 * @param pDisk Pointer to VDI HDD container.
1409 * @param offStart Offset of first reading byte from start of disk.
1410 * @param pvBuf Pointer to buffer for reading data.
1411 * @param cbToRead Number of bytes to read.
1412 */
1413IDER3DECL(int) VDIDiskRead(PVDIDISK pDisk, uint64_t offStart, void *pvBuf, unsigned cbToRead)
1414{
1415 /* sanity check */
1416 Assert(pDisk);
1417 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1418
1419 PVDIIMAGEDESC pImage = pDisk->pLast;
1420 Assert(pImage);
1421
1422 /* Check params. */
1423 if ( offStart + cbToRead > getImageDiskSize(&pImage->Header)
1424 || cbToRead == 0)
1425 {
1426 AssertMsgFailed(("offStart=%llu cbToRead=%u\n", offStart, cbToRead));
1427 return VERR_INVALID_PARAMETER;
1428 }
1429
1430 /* Calculate starting block number and offset inside it. */
1431 unsigned uBlock = (unsigned)(offStart >> pImage->uShiftOffset2Index);
1432 unsigned offRead = (unsigned)offStart & pImage->uBlockMask;
1433
1434 /* Save block size here for speed optimization. */
1435 unsigned cbBlock = getImageBlockSize(&pImage->Header);
1436
1437 /* loop through blocks */
1438 int rc;
1439 for (;;)
1440 {
1441 unsigned to_read;
1442 if ((offRead + cbToRead) <= cbBlock)
1443 to_read = cbToRead;
1444 else
1445 to_read = cbBlock - offRead;
1446
1447 if (pDisk->cImages > 1)
1448 {
1449 /* Differencing images are used, handle them. */
1450 pImage = pDisk->pLast;
1451
1452 /* Search for image with allocated block. */
1453 while (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
1454 {
1455 pImage = pImage->pPrev;
1456 if (!pImage)
1457 {
1458 /* Block is not allocated in all images of chain. */
1459 pImage = pDisk->pLast;
1460 break;
1461 }
1462 }
1463 }
1464
1465 rc = vdiReadInBlock(pImage, uBlock, offRead, to_read, pvBuf);
1466
1467 cbToRead -= to_read;
1468 if ( cbToRead == 0
1469 || VBOX_FAILURE(rc))
1470 break;
1471
1472 /* goto next block */
1473 uBlock++;
1474 offRead = 0;
1475 pvBuf = (char *)pvBuf + to_read;
1476 }
1477
1478 return rc;
1479}
1480
1481/**
1482 * internal: fill the whole block with zeroes.
1483 *
1484 * note: block id must be valid, block must be already allocated in file.
1485 * note: if pDisk is NULL, the default buffer size is used
1486 */
1487static int vdiFillBlockByZeroes(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock)
1488{
1489 int rc;
1490
1491 /* seek to start of block in file. */
1492 uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
1493 + (pImage->offStartData + pImage->offStartBlockData);
1494 rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
1495 if (VBOX_FAILURE(rc))
1496 {
1497 Log(("vdiFillBlockByZeroes: seek rc=%Vrc filename=\"%s\" uBlock=%u u64Offset=%llu\n",
1498 rc, pImage->szFilename, uBlock, u64Offset));
1499 return rc;
1500 }
1501
1502 /* alloc tmp zero-filled buffer */
1503 void *pvBuf = RTMemTmpAllocZ(pDisk ? pDisk->cbBuf : VDIDISK_DEFAULT_BUFFER_SIZE);
1504 if (!pvBuf)
1505 return VERR_NO_MEMORY;
1506
1507 unsigned cbFill = getImageBlockSize(&pImage->Header);
1508
1509 /* do loop, because buffer size may be less then block size */
1510 while (cbFill > 0)
1511 {
1512 unsigned to_fill = RT_MIN(cbFill, pDisk ? pDisk->cbBuf : VDIDISK_DEFAULT_BUFFER_SIZE);
1513 rc = RTFileWrite(pImage->File, pvBuf, to_fill, NULL);
1514 if (VBOX_FAILURE(rc))
1515 {
1516 Log(("vdiFillBlockByZeroes: write rc=%Vrc filename=\"%s\" uBlock=%u u64Offset=%llu cbFill=%u to_fill=%u\n",
1517 rc, pImage->szFilename, uBlock, u64Offset, cbFill, to_fill));
1518 break;
1519 }
1520
1521 cbFill -= to_fill;
1522 }
1523
1524 RTMemTmpFree(pvBuf);
1525 return rc;
1526}
1527
1528/**
1529 * internal: write data inside image block.
1530 *
1531 * note: uBlock must be valid, written data must not overlap block bounds.
1532 */
1533static int vdiWriteInBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offWrite, unsigned cbToWrite, const void *pvBuf)
1534{
1535 int rc;
1536
1537 /* Check if we can write into file. */
1538 if (pImage->fReadOnly)
1539 {
1540 Log(("vdiWriteInBlock: failed, image \"%s\" is read-only!\n", pImage->szFilename));
1541 return VERR_WRITE_PROTECT;
1542 }
1543
1544 vdiSetModifiedFlag(pImage);
1545
1546 if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
1547 {
1548 /* need to allocate a new block in image file */
1549
1550 /* expand file by one block */
1551 uint64_t u64Size = (((uint64_t)(getImageBlocksAllocated(&pImage->Header) + 1)) << pImage->uShiftIndex2Offset)
1552 + pImage->offStartData;
1553 rc = RTFileSetSize(pImage->File, u64Size);
1554 if (VBOX_FAILURE(rc))
1555 {
1556 Log(("vdiWriteInBlock: set size rc=%Vrc filename=\"%s\" uBlock=%u u64Size=%llu\n",
1557 rc, pImage->szFilename, uBlock, u64Size));
1558 return rc;
1559 }
1560
1561 if ( pImage->fFlags & VDI_IMAGE_FLAGS_ZERO_EXPAND
1562 || pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
1563 {
1564 /* Fill newly allocated block by zeroes. */
1565 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
1566 pImage->paBlocks[uBlock] = cBlocksAllocated;
1567 setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
1568
1569 if ( offWrite != 0
1570 || cbToWrite != getImageBlockSize(&pImage->Header))
1571 {
1572 rc = vdiFillBlockByZeroes(pDisk, pImage, uBlock);
1573 if (VBOX_FAILURE(rc))
1574 return rc;
1575 }
1576 }
1577 else
1578 {
1579 /* No need to fill block by zeroes. */
1580 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
1581 pImage->paBlocks[uBlock] = cBlocksAllocated;
1582 setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
1583 }
1584 rc = vdiUpdateBlockInfo(pImage, uBlock);
1585 if (VBOX_FAILURE(rc))
1586 return rc;
1587 }
1588
1589 /* Now block present in image file, write data inside it. */
1590 uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
1591 + (pImage->offStartData + pImage->offStartBlockData + offWrite);
1592 rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
1593 if (VBOX_SUCCESS(rc))
1594 {
1595 rc = RTFileWrite(pImage->File, pvBuf, cbToWrite, NULL);
1596 if (VBOX_FAILURE(rc))
1597 Log(("vdiWriteInBlock: write rc=%Vrc filename=\"%s\" uBlock=%u offWrite=%u u64Offset=%llu cbToWrite=%u\n",
1598 rc, pImage->szFilename, uBlock, offWrite, u64Offset, cbToWrite));
1599 }
1600 else
1601 Log(("vdiWriteInBlock: seek rc=%Vrc filename=\"%s\" uBlock=%u offWrite=%u u64Offset=%llu\n",
1602 rc, pImage->szFilename, uBlock, offWrite, u64Offset));
1603
1604 return rc;
1605}
1606
1607/**
1608 * internal: copy data block from one (parent) image to last image.
1609 */
1610static int vdiCopyBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock)
1611{
1612 Assert(pImage != pDisk->pLast);
1613
1614 if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
1615 {
1616 /*
1617 * if src block is zero, set dst block to zero too.
1618 */
1619 pDisk->pLast->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
1620 return VINF_SUCCESS;
1621 }
1622
1623 /* alloc tmp buffer */
1624 void *pvBuf = RTMemTmpAlloc(pDisk->cbBuf);
1625 if (!pvBuf)
1626 return VERR_NO_MEMORY;
1627
1628 int rc = VINF_SUCCESS;
1629
1630 unsigned cbCopy = getImageBlockSize(&pImage->Header);
1631 unsigned offCopy = 0;
1632
1633 /* do loop, because buffer size may be less then block size */
1634 while (cbCopy > 0)
1635 {
1636 unsigned to_copy = RT_MIN(cbCopy, pDisk->cbBuf);
1637 rc = vdiReadInBlock(pImage, uBlock, offCopy, to_copy, pvBuf);
1638 if (VBOX_FAILURE(rc))
1639 break;
1640
1641 rc = vdiWriteInBlock(pDisk, pDisk->pLast, uBlock, offCopy, to_copy, pvBuf);
1642 if (VBOX_FAILURE(rc))
1643 break;
1644
1645 cbCopy -= to_copy;
1646 offCopy += to_copy;
1647 }
1648
1649 RTMemTmpFree(pvBuf);
1650 return rc;
1651}
1652
1653/**
1654 * Write data to virtual HDD.
1655 *
1656 * @returns VBox status code.
1657 * @param pDisk Pointer to VDI HDD container.
1658 * @param offStart Offset of first writing byte from start of HDD.
1659 * @param pvBuf Pointer to buffer of writing data.
1660 * @param cbToWrite Number of bytes to write.
1661 */
1662IDER3DECL(int) VDIDiskWrite(PVDIDISK pDisk, uint64_t offStart, const void *pvBuf, unsigned cbToWrite)
1663{
1664 /* sanity check */
1665 Assert(pDisk);
1666 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1667
1668 PVDIIMAGEDESC pImage = pDisk->pLast;
1669 Assert(pImage);
1670
1671 /* Check params. */
1672 if ( offStart + cbToWrite > getImageDiskSize(&pImage->Header)
1673 || cbToWrite == 0)
1674 {
1675 AssertMsgFailed(("offStart=%llu cbToWrite=%u\n", offStart, cbToWrite));
1676 return VERR_INVALID_PARAMETER;
1677 }
1678
1679 /* Calculate starting block number and offset inside it. */
1680 unsigned uBlock = (unsigned)(offStart >> pImage->uShiftOffset2Index);
1681 unsigned offWrite = (unsigned)offStart & pImage->uBlockMask;
1682
1683 /* Save block size here for speed optimization. */
1684 unsigned cbBlock = getImageBlockSize(&pImage->Header);
1685
1686 /* loop through blocks */
1687 int rc;
1688 for (;;)
1689 {
1690 unsigned to_write;
1691 if (offWrite + cbToWrite <= cbBlock)
1692 to_write = cbToWrite;
1693 else
1694 to_write = cbBlock - offWrite;
1695
1696 if (pDisk->cImages > 1)
1697 {
1698 /* Differencing images are used, handle them. */
1699
1700 /* Search for image with allocated block. */
1701 while (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
1702 {
1703 pImage = pImage->pPrev;
1704 if (!pImage)
1705 {
1706 /* Block is not allocated in all images of chain. */
1707 pImage = pDisk->pLast;
1708 break;
1709 }
1710 }
1711
1712 if (pImage != pDisk->pLast)
1713 {
1714 /* One of parent image has a block data, copy it into last image. */
1715 rc = vdiCopyBlock(pDisk, pImage, uBlock);
1716 if (VBOX_FAILURE(rc))
1717 break;
1718 pImage = pDisk->pLast;
1719 }
1720 }
1721
1722 /* Actually write the data into block. */
1723 rc = vdiWriteInBlock(pDisk, pImage, uBlock, offWrite, to_write, pvBuf);
1724
1725 cbToWrite -= to_write;
1726 if ( cbToWrite == 0
1727 || VBOX_FAILURE(rc))
1728 break;
1729
1730 /* goto next block */
1731 uBlock++;
1732 offWrite = 0;
1733 pvBuf = (char *)pvBuf + to_write;
1734 }
1735
1736 return rc;
1737}
1738
1739/**
1740 * internal: commit one image to another, no changes to header, just
1741 * plain copy operation. Blocks that are not allocated in the source
1742 * image (i.e. inherited by its parent(s)) are not merged.
1743 *
1744 * @param pImageFrom source image
1745 * @param pImageTo target image (will receive all the modifications)
1746 * @param fParentToChild true if the source image is parent of the target one,
1747 * false of the target image is the parent of the source.
1748 * @param pfnProgress progress callback (NULL if not to be used)
1749 * @param pvUser user argument for the progress callback
1750 *
1751 * @note the target image has to be opened read/write
1752 * @note this method does not check whether merging is possible!
1753 */
1754static int vdiMergeImages(PVDIIMAGEDESC pImageFrom, PVDIIMAGEDESC pImageTo, bool fParentToChild,
1755 PFNVMPROGRESS pfnProgress, void *pvUser)
1756{
1757 Assert(pImageFrom);
1758 Assert(pImageTo);
1759
1760 Log(("vdiMergeImages: merging from image \"%s\" to image \"%s\" (fParentToChild=%d)\n",
1761 pImageFrom->szFilename, pImageTo->szFilename, fParentToChild));
1762
1763 /* alloc tmp buffer */
1764 void *pvBuf = RTMemTmpAlloc(VDIDISK_DEFAULT_BUFFER_SIZE);
1765 if (!pvBuf)
1766 return VERR_NO_MEMORY;
1767
1768 int rc = VINF_SUCCESS;
1769
1770 if (!fParentToChild)
1771 {
1772 /*
1773 * Commit the child image to the parent image.
1774 * Child is the source (from), parent is the target (to).
1775 */
1776
1777 unsigned cBlocks = getImageBlocks(&pImageFrom->Header);
1778
1779 for (unsigned uBlock = 0; uBlock < cBlocks; uBlock++)
1780 {
1781 /* only process blocks that are allocated in the source image */
1782 if (pImageFrom->paBlocks[uBlock] != VDI_IMAGE_BLOCK_FREE)
1783 {
1784 /* Found used block in source image, commit it. */
1785 if ( pImageFrom->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
1786 && !IS_VDI_IMAGE_BLOCK_ALLOCATED(pImageTo->paBlocks[uBlock]))
1787 {
1788 /* Block is zero in the source image and not allocated in the target image. */
1789 pImageTo->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
1790 vdiSetModifiedFlag(pImageTo);
1791 }
1792 else
1793 {
1794 /* Block is not zero / allocated in source image. */
1795 unsigned cbCommit = getImageBlockSize(&pImageFrom->Header);
1796 unsigned offCommit = 0;
1797
1798 /* do loop, because buffer size may be less then block size */
1799 while (cbCommit > 0)
1800 {
1801 unsigned cbToCopy = RT_MIN(cbCommit, VDIDISK_DEFAULT_BUFFER_SIZE);
1802
1803 rc = vdiReadInBlock(pImageFrom, uBlock, offCommit, cbToCopy, pvBuf);
1804 if (VBOX_FAILURE(rc))
1805 break;
1806
1807 rc = vdiWriteInBlock(NULL, pImageTo, uBlock, offCommit, cbToCopy, pvBuf);
1808 if (VBOX_FAILURE(rc))
1809 break;
1810
1811 cbCommit -= cbToCopy;
1812 offCommit += cbToCopy;
1813 }
1814 if (VBOX_FAILURE(rc))
1815 break;
1816 }
1817 }
1818
1819 if (pfnProgress)
1820 {
1821 pfnProgress(NULL /* Achtung! pVM=NULL */,
1822 (uBlock * 100) / cBlocks,
1823 pvUser);
1824 /* Note: commiting is non breakable operation, skipping rc here. */
1825 }
1826 }
1827 }
1828 else
1829 {
1830 /*
1831 * Commit the parent image to the child image.
1832 * Parent is the source (from), child is the target (to).
1833 */
1834
1835 unsigned cBlocks = getImageBlocks(&pImageFrom->Header);
1836
1837 for (unsigned uBlock = 0; uBlock < cBlocks; uBlock++)
1838 {
1839 /*
1840 * only process blocks that are allocated or zero in the source image
1841 * and NEITHER allocated NOR zero in the target image
1842 */
1843 if (pImageFrom->paBlocks[uBlock] != VDI_IMAGE_BLOCK_FREE &&
1844 pImageTo->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
1845 {
1846 /* Found used block in source image (but unused in target), commit it. */
1847 if ( pImageFrom->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
1848 {
1849 /* Block is zero in the source image and not allocated in the target image. */
1850 pImageTo->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
1851 vdiSetModifiedFlag(pImageTo);
1852 }
1853 else
1854 {
1855 /* Block is not zero / allocated in source image. */
1856 unsigned cbCommit = getImageBlockSize(&pImageFrom->Header);
1857 unsigned offCommit = 0;
1858
1859 /* do loop, because buffer size may be less then block size */
1860 while (cbCommit > 0)
1861 {
1862 unsigned cbToCopy = RT_MIN(cbCommit, VDIDISK_DEFAULT_BUFFER_SIZE);
1863
1864 rc = vdiReadInBlock(pImageFrom, uBlock, offCommit, cbToCopy, pvBuf);
1865 if (VBOX_FAILURE(rc))
1866 break;
1867
1868 rc = vdiWriteInBlock(NULL, pImageTo, uBlock, offCommit, cbToCopy, pvBuf);
1869 if (VBOX_FAILURE(rc))
1870 break;
1871
1872 cbCommit -= cbToCopy;
1873 offCommit += cbToCopy;
1874 }
1875 if (VBOX_FAILURE(rc))
1876 break;
1877 }
1878 }
1879
1880 if (pfnProgress)
1881 {
1882 pfnProgress(NULL /* Achtung! pVM=NULL */,
1883 (uBlock * 100) / cBlocks,
1884 pvUser);
1885 /* Note: commiting is non breakable operation, skipping rc here. */
1886 }
1887 }
1888 }
1889
1890 RTMemTmpFree(pvBuf);
1891 return rc;
1892}
1893
1894/**
1895 * internal: commit last image(s) to selected previous image.
1896 * note: all images accessed across this call must be opened in R/W mode.
1897 */
1898static int vdiCommitToImage(PVDIDISK pDisk, PVDIIMAGEDESC pDstImage,
1899 PFNVMPROGRESS pfnProgress, void *pvUser)
1900{
1901 /* sanity check */
1902 Assert(pDisk);
1903 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1904 Assert(pDstImage);
1905
1906 PVDIIMAGEDESC pImage = pDisk->pLast;
1907 Assert(pImage);
1908 Log(("vdiCommitToImage: commiting from image \"%s\" to image \"%s\"\n",
1909 pImage->szFilename, pDstImage->szFilename));
1910 if (pDstImage == pImage)
1911 {
1912 Log(("vdiCommitToImage: attempt to commit to the same image!\n"));
1913 return VERR_VDI_NO_DIFF_IMAGES;
1914 }
1915
1916 /* Scan images for pDstImage. */
1917 while (pImage && pImage != pDstImage)
1918 pImage = pImage->pPrev;
1919 if (!pImage)
1920 {
1921 AssertMsgFailed(("Invalid arguments: pDstImage is not in images chain\n"));
1922 return VERR_INVALID_PARAMETER;
1923 }
1924 pImage = pDisk->pLast;
1925
1926 /* alloc tmp buffer */
1927 void *pvBuf = RTMemTmpAlloc(pDisk->cbBuf);
1928 if (!pvBuf)
1929 return VERR_NO_MEMORY;
1930
1931 int rc = VINF_SUCCESS;
1932 unsigned cBlocks = getImageBlocks(&pImage->Header);
1933
1934 for (unsigned uBlock = 0; uBlock < cBlocks; uBlock++)
1935 {
1936 pImage = pDisk->pLast;
1937
1938 /* Find allocated block to commit. */
1939 while ( pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE
1940 && pImage != pDstImage)
1941 pImage = pImage->pPrev;
1942
1943 if (pImage != pDstImage)
1944 {
1945 /* Found used block in diff image (pImage), commit it. */
1946 if ( pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
1947 && !IS_VDI_IMAGE_BLOCK_ALLOCATED(pDstImage->paBlocks[uBlock]))
1948 {
1949 /* Block is zero in difference image and not allocated in primary image. */
1950 pDstImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
1951 vdiSetModifiedFlag(pDstImage);
1952 }
1953 else
1954 {
1955 /* Block is not zero / allocated in primary image. */
1956 unsigned cbCommit = getImageBlockSize(&pImage->Header);
1957 unsigned offCommit = 0;
1958
1959 /* do loop, because buffer size may be less then block size */
1960 while (cbCommit > 0)
1961 {
1962 unsigned cbToCopy = RT_MIN(cbCommit, pDisk->cbBuf);
1963
1964 rc = vdiReadInBlock(pImage, uBlock, offCommit, cbToCopy, pvBuf);
1965 if (VBOX_FAILURE(rc))
1966 break;
1967
1968 rc = vdiWriteInBlock(pDisk, pDstImage, uBlock, offCommit, cbToCopy, pvBuf);
1969 if (VBOX_FAILURE(rc))
1970 break;
1971
1972 cbCommit -= cbToCopy;
1973 offCommit += cbToCopy;
1974 }
1975 if (VBOX_FAILURE(rc))
1976 break;
1977 }
1978 pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_FREE;
1979 }
1980
1981 if (pfnProgress)
1982 {
1983 pfnProgress(NULL /* Achtung! pVM=NULL */,
1984 (uBlock * 100) / cBlocks,
1985 pvUser);
1986 /* Note: commiting is non breakable operation, skipping rc here. */
1987 }
1988 }
1989
1990 RTMemTmpFree(pvBuf);
1991
1992 /* Go forward and update linkage information. */
1993 for (pImage = pDstImage; pImage; pImage = pImage->pNext)
1994 {
1995 /* generate new last-modified uuid. */
1996 RTUuidCreate(getImageModificationUUID(&pImage->Header));
1997
1998 /* fix up linkage. */
1999 if (pImage != pDstImage)
2000 *getImageParentModificationUUID(&pImage->Header) = *getImageModificationUUID(&pImage->pPrev->Header);
2001
2002 /* reset modified flag. */
2003 pImage->fModified = 0;
2004 }
2005
2006 /* Process committed images - truncate them. */
2007 for (pImage = pDisk->pLast; pImage != pDstImage; pImage = pImage->pPrev)
2008 {
2009 /* note: can't understand how to do error works here? */
2010
2011 setImageBlocksAllocated(&pImage->Header, 0);
2012
2013 /* Truncate file. */
2014 int rc2 = RTFileSetSize(pImage->File, pImage->offStartData);
2015 if (VBOX_FAILURE(rc2))
2016 {
2017 rc = rc2;
2018 Log(("vdiCommitToImage: set size (truncate) rc=%Vrc filename=\"%s\"\n",
2019 rc, pImage->szFilename));
2020 }
2021
2022 /* Save header and blocks array. */
2023 rc2 = vdiUpdateBlocks(pImage);
2024 if (VBOX_FAILURE(rc2))
2025 {
2026 rc = rc2;
2027 Log(("vdiCommitToImage: update blocks and header rc=%Vrc filename=\"%s\"\n",
2028 rc, pImage->szFilename));
2029 }
2030 }
2031
2032 if (pfnProgress)
2033 {
2034 pfnProgress(NULL /* Achtung! pVM=NULL */, 100, pvUser);
2035 /* Note: commiting is non breakable operation, skipping rc here. */
2036 }
2037
2038 Log(("vdiCommitToImage: done, rc=%Vrc\n", rc));
2039
2040 return rc;
2041}
2042
2043/**
2044 * Checks if image is available and not broken, returns some useful image parameters if requested.
2045 *
2046 * @returns VBox status code.
2047 * @param pszFilename Name of the image file to check.
2048 * @param puVersion Where to store the version of image. NULL is ok.
2049 * @param penmType Where to store the type of image. NULL is ok.
2050 * @param pcbSize Where to store the size of image in bytes. NULL is ok.
2051 * @param pUuid Where to store the uuid of image creation. NULL is ok.
2052 * @param pParentUuid Where to store the UUID of the parent image. NULL is ok.
2053 * @param pszComment Where to store the comment string of image. NULL is ok.
2054 * @param cbComment The size of pszComment buffer. 0 is ok.
2055 */
2056IDER3DECL(int) VDICheckImage(const char *pszFilename,
2057 unsigned *puVersion,
2058 PVDIIMAGETYPE penmType,
2059 uint64_t *pcbSize,
2060 PRTUUID pUuid,
2061 PRTUUID pParentUuid,
2062 char *pszComment,
2063 unsigned cbComment)
2064{
2065 LogFlow(("VDICheckImage:\n"));
2066
2067 /* Check arguments. */
2068 if ( !pszFilename
2069 || *pszFilename == '\0')
2070 {
2071 AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
2072 return VERR_INVALID_PARAMETER;
2073 }
2074
2075 PVDIIMAGEDESC pImage;
2076 int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_READONLY, NULL);
2077 if (VBOX_SUCCESS(rc))
2078 {
2079 Log(("VDICheckImage: filename=\"%s\" version=%08X type=%X cbDisk=%llu uuid={%Vuuid}\n",
2080 pszFilename,
2081 pImage->PreHeader.u32Version,
2082 getImageType(&pImage->Header),
2083 getImageDiskSize(&pImage->Header),
2084 getImageCreationUUID(&pImage->Header)));
2085
2086 if ( pszComment
2087 && cbComment > 0)
2088 {
2089 char *pszTmp = getImageComment(&pImage->Header);
2090 unsigned cb = strlen(pszTmp);
2091 if (cbComment > cb)
2092 memcpy(pszComment, pszTmp, cb + 1);
2093 else
2094 rc = VERR_BUFFER_OVERFLOW;
2095 }
2096 if (VBOX_SUCCESS(rc))
2097 {
2098 if (puVersion)
2099 *puVersion = pImage->PreHeader.u32Version;
2100 if (penmType)
2101 *penmType = getImageType(&pImage->Header);
2102 if (pcbSize)
2103 *pcbSize = getImageDiskSize(&pImage->Header);
2104 if (pUuid)
2105 *pUuid = *getImageCreationUUID(&pImage->Header);
2106 if (pParentUuid)
2107 *pParentUuid = *getImageParentUUID(&pImage->Header);
2108 }
2109 vdiCloseImage(pImage);
2110 }
2111
2112 LogFlow(("VDICheckImage: returns %Vrc\n", rc));
2113 return rc;
2114}
2115
2116/**
2117 * Changes an image's comment string.
2118 *
2119 * @returns VBox status code.
2120 * @param pszFilename Name of the image file to operate on.
2121 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
2122 */
2123IDER3DECL(int) VDISetImageComment(const char *pszFilename, const char *pszComment)
2124{
2125 LogFlow(("VDISetImageComment:\n"));
2126
2127 /*
2128 * Validate arguments.
2129 */
2130 if ( !pszFilename
2131 || *pszFilename == '\0')
2132 {
2133 AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
2134 return VERR_INVALID_PARAMETER;
2135 }
2136
2137 const size_t cchComment = pszComment ? strlen(pszComment) : 0;
2138 if (cchComment >= VDI_IMAGE_COMMENT_SIZE)
2139 {
2140 Log(("VDISetImageComment: pszComment is too long, %d bytes!\n", cchComment));
2141 return VERR_VDI_COMMENT_TOO_LONG;
2142 }
2143
2144 /*
2145 * Open the image for updating.
2146 */
2147 PVDIIMAGEDESC pImage;
2148 int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
2149 if (VBOX_FAILURE(rc))
2150 {
2151 Log(("VDISetImageComment: vdiOpenImage rc=%Vrc filename=\"%s\"!\n", rc, pszFilename));
2152 return rc;
2153 }
2154 if (!pImage->fReadOnly)
2155 {
2156 /* we don't support old style images */
2157 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2158 {
2159 /*
2160 * Update the comment field, making sure to zero out all of the previous comment.
2161 */
2162 memset(pImage->Header.u.v1.szComment, '\0', VDI_IMAGE_COMMENT_SIZE);
2163 memcpy(pImage->Header.u.v1.szComment, pszComment, cchComment);
2164
2165 /* write out new the header */
2166 rc = vdiUpdateHeader(pImage);
2167 AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
2168 pImage->szFilename, rc));
2169 }
2170 else
2171 {
2172 Log(("VDISetImageComment: Unsupported version!\n"));
2173 rc = VERR_VDI_UNSUPPORTED_VERSION;
2174 }
2175 }
2176 else
2177 {
2178 Log(("VDISetImageComment: image \"%s\" is opened as read-only!\n", pszFilename));
2179 rc = VERR_VDI_IMAGE_READ_ONLY;
2180 }
2181
2182 vdiCloseImage(pImage);
2183 return rc;
2184}
2185
2186/**
2187 * Creates a new base image file.
2188 *
2189 * @returns VBox status code.
2190 * @param pszFilename Name of the creating image file.
2191 * @param enmType Image type, only base image types are acceptable.
2192 * @param cbSize Image size in bytes.
2193 * @param pszComment Pointer to image comment. NULL is ok.
2194 * @param pfnProgress Progress callback. Optional.
2195 * @param pvUser User argument for the progress callback.
2196 */
2197IDER3DECL(int) VDICreateBaseImage(const char *pszFilename, VDIIMAGETYPE enmType, uint64_t cbSize, const char *pszComment,
2198 PFNVMPROGRESS pfnProgress, void *pvUser)
2199{
2200 LogFlow(("VDICreateBaseImage:\n"));
2201
2202 /* Check arguments. */
2203 if ( !pszFilename
2204 || *pszFilename == '\0'
2205 || (enmType != VDI_IMAGE_TYPE_NORMAL && enmType != VDI_IMAGE_TYPE_FIXED)
2206 || cbSize < VDI_IMAGE_DEFAULT_BLOCK_SIZE)
2207 {
2208 AssertMsgFailed(("Invalid arguments: pszFilename=%p enmType=%x cbSize=%llu\n",
2209 pszFilename, enmType, cbSize));
2210 return VERR_INVALID_PARAMETER;
2211 }
2212
2213 int rc = vdiCreateImage(pszFilename, enmType, VDI_IMAGE_FLAGS_DEFAULT, cbSize, pszComment, NULL,
2214 pfnProgress, pvUser);
2215 LogFlow(("VDICreateBaseImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
2216 return rc;
2217}
2218
2219/**
2220 * Creates a differencing dynamically growing image file for specified parent image.
2221 *
2222 * @returns VBox status code.
2223 * @param pszFilename Name of the creating differencing image file.
2224 * @param pszParent Name of the parent image file. May be base or diff image type.
2225 * @param pszComment Pointer to image comment. NULL is ok.
2226 * @param pfnProgress Progress callback. Optional.
2227 * @param pvUser User argument for the progress callback.
2228 */
2229IDER3DECL(int) VDICreateDifferenceImage(const char *pszFilename, const char *pszParent, const char *pszComment,
2230 PFNVMPROGRESS pfnProgress, void *pvUser)
2231{
2232 LogFlow(("VDICreateDifferenceImage:\n"));
2233
2234 /* Check arguments. */
2235 if ( !pszFilename
2236 || *pszFilename == '\0'
2237 || !pszParent
2238 || *pszParent == '\0')
2239 {
2240 AssertMsgFailed(("Invalid arguments: pszFilename=%p pszParent=%p\n",
2241 pszFilename, pszParent));
2242 return VERR_INVALID_PARAMETER;
2243 }
2244
2245 PVDIIMAGEDESC pParent;
2246 int rc = vdiOpenImage(&pParent, pszParent, VDI_OPEN_FLAGS_READONLY, NULL);
2247 if (VBOX_SUCCESS(rc))
2248 {
2249 rc = vdiCreateImage(pszFilename, VDI_IMAGE_TYPE_DIFF, VDI_IMAGE_FLAGS_DEFAULT,
2250 getImageDiskSize(&pParent->Header), pszComment, pParent,
2251 pfnProgress, pvUser);
2252 vdiCloseImage(pParent);
2253 }
2254 LogFlow(("VDICreateDifferenceImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
2255 return rc;
2256}
2257
2258/**
2259 * Deletes an image. Only valid image files can be deleted by this call.
2260 *
2261 * @returns VBox status code.
2262 * @param pszFilename Name of the image file to check.
2263 */
2264IDER3DECL(int) VDIDeleteImage(const char *pszFilename)
2265{
2266 LogFlow(("VDIDeleteImage:\n"));
2267 /* Check arguments. */
2268 if ( !pszFilename
2269 || *pszFilename == '\0')
2270 {
2271 AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
2272 return VERR_INVALID_PARAMETER;
2273 }
2274
2275 int rc = VDICheckImage(pszFilename, NULL, NULL, NULL, NULL, NULL, NULL, 0);
2276 if (VBOX_SUCCESS(rc))
2277 rc = RTFileDelete(pszFilename);
2278
2279 LogFlow(("VDIDeleteImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
2280 return rc;
2281}
2282
2283/**
2284 * Makes a copy of image file with a new (other) creation uuid.
2285 *
2286 * @returns VBox status code.
2287 * @param pszDstFilename Name of the image file to create.
2288 * @param pszSrcFilename Name of the image file to copy from.
2289 * @param pszComment Pointer to image comment. If NULL specified comment
2290 * will be copied from source image.
2291 * @param pfnProgress Progress callback. Optional.
2292 * @param pvUser User argument for the progress callback.
2293 */
2294IDER3DECL(int) VDICopyImage(const char *pszDstFilename, const char *pszSrcFilename, const char *pszComment,
2295 PFNVMPROGRESS pfnProgress, void *pvUser)
2296{
2297 LogFlow(("VDICopyImage:\n"));
2298
2299 /* Check arguments. */
2300 if ( !pszDstFilename
2301 || *pszDstFilename == '\0'
2302 || !pszSrcFilename
2303 || *pszSrcFilename == '\0')
2304 {
2305 AssertMsgFailed(("Invalid arguments: pszDstFilename=%p pszSrcFilename=%p\n",
2306 pszDstFilename, pszSrcFilename));
2307 return VERR_INVALID_PARAMETER;
2308 }
2309
2310 /* Special check for comment length. */
2311 if ( pszComment
2312 && strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
2313 {
2314 Log(("VDICopyImage: pszComment is too long, cb=%d\n", strlen(pszComment)));
2315 return VERR_VDI_COMMENT_TOO_LONG;
2316 }
2317
2318 PVDIIMAGEDESC pImage;
2319 int rc = vdiOpenImage(&pImage, pszSrcFilename, VDI_OPEN_FLAGS_READONLY, NULL);
2320 if (VBOX_FAILURE(rc))
2321 {
2322 Log(("VDICopyImage: src image \"%s\" open failed rc=%Vrc\n", pszSrcFilename, rc));
2323 return rc;
2324 }
2325
2326 uint64_t cbFile = pImage->offStartData
2327 + ((uint64_t)getImageBlocksAllocated(&pImage->Header) << pImage->uShiftIndex2Offset);
2328
2329 /* create file */
2330 RTFILE File;
2331 rc = RTFileOpen(&File,
2332 pszDstFilename,
2333 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
2334 if (VBOX_SUCCESS(rc))
2335 {
2336 /* lock new image exclusively to close any wrong access by VDI API calls. */
2337 rc = RTFileLock(File, RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY, 0, cbFile);
2338 if (VBOX_SUCCESS(rc))
2339 {
2340 /* Set the size of a new file. */
2341 rc = RTFileSetSize(File, cbFile);
2342 if (VBOX_SUCCESS(rc))
2343 {
2344 /* A dirty trick - use original image data to fill the new image. */
2345 RTFILE oldFileHandle = pImage->File;
2346 pImage->File = File;
2347 pImage->fReadOnly = false;
2348
2349 /* generate a new image creation uuid. */
2350 RTUuidCreate(getImageCreationUUID(&pImage->Header));
2351 /* generate a new image last-modified uuid. */
2352 RTUuidCreate(getImageModificationUUID(&pImage->Header));
2353 /* set image comment, if present. */
2354 if (pszComment)
2355 strncpy(getImageComment(&pImage->Header), pszComment, VDI_IMAGE_COMMENT_SIZE);
2356
2357 /* Write the pre-header to new image. */
2358 rc = RTFileSeek(pImage->File, 0, RTFILE_SEEK_BEGIN, NULL);
2359 if (VBOX_SUCCESS(rc))
2360 rc = RTFileWrite(pImage->File,
2361 &pImage->PreHeader,
2362 sizeof(pImage->PreHeader),
2363 NULL);
2364
2365 /* Write the header and the blocks array to new image. */
2366 if (VBOX_SUCCESS(rc))
2367 rc = vdiUpdateBlocks(pImage);
2368
2369 pImage->File = oldFileHandle;
2370 pImage->fReadOnly = true;
2371
2372 /* Seek to the data start in both images. */
2373 if (VBOX_SUCCESS(rc))
2374 rc = RTFileSeek(pImage->File,
2375 pImage->offStartData,
2376 RTFILE_SEEK_BEGIN,
2377 NULL);
2378 if (VBOX_SUCCESS(rc))
2379 rc = RTFileSeek(File,
2380 pImage->offStartData,
2381 RTFILE_SEEK_BEGIN,
2382 NULL);
2383
2384 if (VBOX_SUCCESS(rc))
2385 {
2386 /* alloc tmp buffer */
2387 void *pvBuf = RTMemTmpAlloc(VDIDISK_DEFAULT_BUFFER_SIZE);
2388 if (pvBuf)
2389 {
2390 /* Main copy loop. */
2391 uint64_t cbData = cbFile - pImage->offStartData;
2392 unsigned cBlocks = (unsigned)(cbData / VDIDISK_DEFAULT_BUFFER_SIZE);
2393 unsigned c = 0;
2394
2395 while (cbData)
2396 {
2397 unsigned cbToCopy = (unsigned)RT_MIN(cbData, VDIDISK_DEFAULT_BUFFER_SIZE);
2398
2399 /* Read. */
2400 rc = RTFileRead(pImage->File, pvBuf, cbToCopy, NULL);
2401 if (VBOX_FAILURE(rc))
2402 break;
2403
2404 /* Write. */
2405 rc = RTFileWrite(File, pvBuf, cbToCopy, NULL);
2406 if (VBOX_FAILURE(rc))
2407 break;
2408
2409 if (pfnProgress)
2410 {
2411 c++;
2412 rc = pfnProgress(NULL /* Achtung! pVM=NULL */,
2413 (c * 100) / cBlocks,
2414 pvUser);
2415 if (VBOX_FAILURE(rc))
2416 break;
2417 }
2418 cbData -= cbToCopy;
2419 }
2420
2421 RTMemTmpFree(pvBuf);
2422 }
2423 else
2424 rc = VERR_NO_MEMORY;
2425 }
2426 }
2427
2428 RTFileUnlock(File, 0, cbFile);
2429 }
2430
2431 RTFileClose(File);
2432
2433 if (VBOX_FAILURE(rc))
2434 RTFileDelete(pszDstFilename);
2435
2436 if (pfnProgress)
2437 pfnProgress(NULL /* Achtung! pVM=NULL */, 100, pvUser);
2438 }
2439
2440 vdiCloseImage(pImage);
2441
2442 LogFlow(("VDICopyImage: returns %Vrc for pszSrcFilename=\"%s\" pszDstFilename=\"%s\"\n",
2443 rc, pszSrcFilename, pszDstFilename));
2444 return rc;
2445}
2446
2447/**
2448 * Shrinks growing image file by removing zeroed data blocks.
2449 *
2450 * @returns VBox status code.
2451 * @param pszFilename Name of the image file to shrink.
2452 * @param pfnProgress Progress callback. Optional.
2453 * @param pvUser User argument for the progress callback.
2454 */
2455IDER3DECL(int) VDIShrinkImage(const char *pszFilename, PFNVMPROGRESS pfnProgress, void *pvUser)
2456{
2457 LogFlow(("VDIShrinkImage:\n"));
2458
2459 /* Check arguments. */
2460 if ( !pszFilename
2461 || *pszFilename == '\0')
2462 {
2463 AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
2464 return VERR_INVALID_PARAMETER;
2465 }
2466
2467 PVDIIMAGEDESC pImage;
2468 int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
2469 if (VBOX_FAILURE(rc))
2470 {
2471 Log(("VDIShrinkImage: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
2472 return rc;
2473 }
2474 if (pImage->fReadOnly)
2475 {
2476 Log(("VDIShrinkImage: image \"%s\" is opened as read-only!\n", pszFilename));
2477 vdiCloseImage(pImage);
2478 return VERR_VDI_IMAGE_READ_ONLY;
2479 }
2480
2481 /* Do debug dump. */
2482 vdiDumpImage(pImage);
2483
2484 /* Working data. */
2485 unsigned cbBlock = getImageBlockSize(&pImage->Header);
2486 unsigned cBlocks = getImageBlocks(&pImage->Header);
2487 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
2488
2489 uint64_t cbFile;
2490 rc = RTFileGetSize(pImage->File, &cbFile);
2491 if (VBOX_FAILURE(rc))
2492 {
2493 Log(("VDIShrinkImage: RTFileGetSize rc=%Vrc for file=\"%s\"\n", rc, pszFilename));
2494 vdiCloseImage(pImage);
2495 return rc;
2496 }
2497
2498 uint64_t cbData = cbFile - pImage->offStartData;
2499 unsigned cBlocksAllocated2 = (unsigned)(cbData >> pImage->uShiftIndex2Offset);
2500 if (cbData != (uint64_t)cBlocksAllocated << pImage->uShiftIndex2Offset)
2501 Log(("VDIShrinkImage: invalid image file length, cbBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
2502 cbBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
2503
2504 /* Allocate second blocks array for back resolving. */
2505 PVDIIMAGEBLOCKPOINTER paBlocks2 = (PVDIIMAGEBLOCKPOINTER)RTMemTmpAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * cBlocks);
2506 if (!paBlocks2)
2507 {
2508 Log(("VDIShrinkImage: failed to allocate paBlocks2 buffer (%u bytes)\n",
2509 sizeof(VDIIMAGEBLOCKPOINTER) * cBlocks));
2510 vdiCloseImage(pImage);
2511 return VERR_NO_MEMORY;
2512 }
2513
2514 /* Init second blocks array. */
2515 for (unsigned n = 0; n < cBlocks; n++)
2516 paBlocks2[n] = VDI_IMAGE_BLOCK_FREE;
2517
2518 /* Fill second blocks array, check for allocational errors. */
2519 for (unsigned n = 0; n < cBlocks; n++)
2520 {
2521 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[n]))
2522 {
2523 unsigned uBlock = pImage->paBlocks[n];
2524 if (uBlock < cBlocksAllocated2)
2525 {
2526 if (paBlocks2[uBlock] == VDI_IMAGE_BLOCK_FREE)
2527 paBlocks2[uBlock] = n;
2528 else
2529 {
2530 Log(("VDIShrinkImage: block n=%u -> uBlock=%u is already in use!\n", n, uBlock));
2531 /* free second link to block. */
2532 pImage->paBlocks[n] = VDI_IMAGE_BLOCK_FREE;
2533 }
2534 }
2535 else
2536 {
2537 Log(("VDIShrinkImage: block n=%u -> uBlock=%u is out of blocks range! (cbBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu)\n",
2538 n, uBlock, cbBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
2539 /* free link to invalid block. */
2540 pImage->paBlocks[n] = VDI_IMAGE_BLOCK_FREE;
2541 }
2542 }
2543 }
2544
2545 /* Allocate a working buffer for one block. */
2546 void *pvBuf = RTMemTmpAlloc(cbBlock);
2547 if (pvBuf)
2548 {
2549 /* Main voodoo loop, search holes and fill it. */
2550 unsigned uBlockWrite = 0;
2551 for (unsigned uBlock = 0; uBlock < cBlocksAllocated2; uBlock++)
2552 {
2553 if (paBlocks2[uBlock] != VDI_IMAGE_BLOCK_FREE)
2554 {
2555 /* Read the block from file and check for zeroes. */
2556 uint64_t u64Offset = ((uint64_t)uBlock << pImage->uShiftIndex2Offset)
2557 + (pImage->offStartData + pImage->offStartBlockData);
2558 rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
2559 if (VBOX_FAILURE(rc))
2560 {
2561 Log(("VDIShrinkImage: seek rc=%Vrc filename=\"%s\" uBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
2562 rc, pImage->szFilename, uBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
2563 break;
2564 }
2565 rc = RTFileRead(pImage->File, pvBuf, cbBlock, NULL);
2566 if (VBOX_FAILURE(rc))
2567 {
2568 Log(("VDIShrinkImage: read rc=%Vrc filename=\"%s\" cbBlock=%u uBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
2569 rc, pImage->szFilename, cbBlock, uBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
2570 break;
2571 }
2572
2573 /* Check block for data. */
2574 bool flBlockZeroed = true; /* Block is zeroed flag. */
2575 for (unsigned i = 0; i < (cbBlock >> 2); i++)
2576 if (((uint32_t *)pvBuf)[i] != 0)
2577 {
2578 /* Block is not zeroed! */
2579 flBlockZeroed = false;
2580 break;
2581 }
2582
2583 if (!flBlockZeroed)
2584 {
2585 /* Block has a data, may be it must be moved. */
2586 if (uBlockWrite < uBlock)
2587 {
2588 /* Move the block. */
2589 u64Offset = ((uint64_t)uBlockWrite << pImage->uShiftIndex2Offset)
2590 + (pImage->offStartData + pImage->offStartBlockData);
2591 rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
2592 if (VBOX_FAILURE(rc))
2593 {
2594 Log(("VDIShrinkImage: seek(2) rc=%Vrc filename=\"%s\" uBlockWrite=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
2595 rc, pImage->szFilename, uBlockWrite, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
2596 break;
2597 }
2598 rc = RTFileWrite(pImage->File, pvBuf, cbBlock, NULL);
2599 if (VBOX_FAILURE(rc))
2600 {
2601 Log(("VDIShrinkImage: write rc=%Vrc filename=\"%s\" cbBlock=%u uBlockWrite=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
2602 rc, pImage->szFilename, cbBlock, uBlockWrite, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
2603 break;
2604 }
2605 }
2606 /* Fix the block pointer. */
2607 pImage->paBlocks[paBlocks2[uBlock]] = uBlockWrite;
2608 uBlockWrite++;
2609 }
2610 else
2611 {
2612 Log(("VDIShrinkImage: found a zeroed block, uBlock=%u\n", uBlock));
2613
2614 /* Fix the block pointer. */
2615 pImage->paBlocks[paBlocks2[uBlock]] = VDI_IMAGE_BLOCK_ZERO;
2616 }
2617 }
2618 else
2619 Log(("VDIShrinkImage: found an unused block, uBlock=%u\n", uBlock));
2620
2621 if (pfnProgress)
2622 {
2623 pfnProgress(NULL /* Achtung! pVM=NULL */,
2624 (uBlock * 100) / cBlocksAllocated2,
2625 pvUser);
2626 /* Shrink is unbreakable operation! */
2627 }
2628 }
2629
2630 RTMemTmpFree(pvBuf);
2631
2632 if ( VBOX_SUCCESS(rc)
2633 && uBlockWrite < cBlocksAllocated2)
2634 {
2635 /* File size must be shrinked. */
2636 Log(("VDIShrinkImage: shrinking file size from %llu to %llu bytes\n",
2637 cbFile,
2638 pImage->offStartData + ((uint64_t)uBlockWrite << pImage->uShiftIndex2Offset)));
2639 rc = RTFileSetSize(pImage->File,
2640 pImage->offStartData + ((uint64_t)uBlockWrite << pImage->uShiftIndex2Offset));
2641 if (VBOX_FAILURE(rc))
2642 Log(("VDIShrinkImage: RTFileSetSize rc=%Vrc\n", rc));
2643 }
2644 cBlocksAllocated2 = uBlockWrite;
2645 }
2646 else
2647 {
2648 Log(("VDIShrinkImage: failed to allocate working buffer (%u bytes)\n", cbBlock));
2649 rc = VERR_NO_MEMORY;
2650 }
2651
2652 /* Save header and blocks array. */
2653 if (VBOX_SUCCESS(rc))
2654 {
2655 setImageBlocksAllocated(&pImage->Header, cBlocksAllocated2);
2656 rc = vdiUpdateBlocks(pImage);
2657 if (pfnProgress)
2658 pfnProgress(NULL /* Achtung! pVM=NULL */, 100, pvUser);
2659 }
2660
2661 /* Do debug dump. */
2662 vdiDumpImage(pImage);
2663
2664 /* Clean up. */
2665 RTMemTmpFree(paBlocks2);
2666 vdiCloseImage(pImage);
2667
2668 LogFlow(("VDIShrinkImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
2669 return rc;
2670}
2671
2672/**
2673 * Converts image file from older VDI formats to current one.
2674 *
2675 * @returns VBox status code.
2676 * @param pszFilename Name of the image file to convert.
2677 * @param pfnProgress Progress callback. Optional.
2678 * @param pvUser User argument for the progress callback.
2679 */
2680IDER3DECL(int) VDIConvertImage(const char *pszFilename, PFNVMPROGRESS pfnProgress, void *pvUser)
2681{
2682 LogFlow(("VDIConvertImage:\n"));
2683
2684 /* Check arguments. */
2685 if ( !pszFilename
2686 || *pszFilename == '\0')
2687 {
2688 AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
2689 return VERR_INVALID_PARAMETER;
2690 }
2691
2692 PVDIIMAGEDESC pImage;
2693 int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
2694 if (VBOX_FAILURE(rc))
2695 {
2696 Log(("VDIConvertImage: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
2697 return rc;
2698 }
2699
2700 VDIHEADER Header = {0};
2701 int off;
2702 uint64_t cbFile;
2703 uint64_t cbData;
2704
2705 if (pImage->fReadOnly)
2706 {
2707 Log(("VDIConvertImage: image \"%s\" is opened as read-only!\n", pszFilename));
2708 rc = VERR_VDI_IMAGE_READ_ONLY;
2709 goto l_conversion_failed;
2710 }
2711
2712 if (pImage->PreHeader.u32Version != 0x00000002)
2713 {
2714 Log(("VDIConvertImage: unsupported version=%08X filename=\"%s\"\n",
2715 pImage->PreHeader.u32Version, pszFilename));
2716 rc = VERR_VDI_UNSUPPORTED_VERSION;
2717 goto l_conversion_failed;
2718 }
2719
2720 /* Build new version header from old one. */
2721 vdiInitHeader(&Header,
2722 getImageType(&pImage->Header),
2723 VDI_IMAGE_FLAGS_DEFAULT, /* Safety issue: Always use default flags. */
2724 getImageComment(&pImage->Header),
2725 getImageDiskSize(&pImage->Header),
2726 getImageBlockSize(&pImage->Header),
2727 0);
2728 setImageBlocksAllocated(&Header, getImageBlocksAllocated(&pImage->Header));
2729 *getImageGeometry(&Header) = *getImageGeometry(&pImage->Header);
2730 setImageTranslation(&Header, getImageTranslation(&pImage->Header));
2731 *getImageCreationUUID(&Header) = *getImageCreationUUID(&pImage->Header);
2732 *getImageModificationUUID(&Header) = *getImageModificationUUID(&pImage->Header);
2733
2734 /* Calc data offset. */
2735 off = getImageDataOffset(&Header) - getImageDataOffset(&pImage->Header);
2736 if (off <= 0)
2737 {
2738 rc = VERR_VDI_INVALID_HEADER;
2739 goto l_conversion_failed;
2740 }
2741
2742 rc = RTFileGetSize(pImage->File, &cbFile);
2743 if (VBOX_FAILURE(rc))
2744 goto l_conversion_failed;
2745
2746 /* Check file size. */
2747 cbData = cbFile - getImageDataOffset(&pImage->Header);
2748 if (cbData != (uint64_t)getImageBlocksAllocated(&pImage->Header) << pImage->uShiftIndex2Offset)
2749 {
2750 AssertMsgFailed(("Invalid file size, broken image?\n"));
2751 rc = VERR_VDI_INVALID_HEADER;
2752 goto l_conversion_failed;
2753 }
2754
2755 /* Expand file. */
2756 rc = RTFileSetSize(pImage->File, cbFile + off);
2757 if (VBOX_FAILURE(rc))
2758 goto l_conversion_failed;
2759
2760 if (cbData > 0)
2761 {
2762 /* Calc current file position to move data from. */
2763 uint64_t offFile;
2764 if (cbData > VDIDISK_DEFAULT_BUFFER_SIZE)
2765 offFile = cbFile - VDIDISK_DEFAULT_BUFFER_SIZE;
2766 else
2767 offFile = getImageDataOffset(&pImage->Header);
2768
2769 unsigned cMoves = (unsigned)(cbData / VDIDISK_DEFAULT_BUFFER_SIZE);
2770 unsigned c = 0;
2771
2772 /* alloc tmp buffer */
2773 void *pvBuf = RTMemTmpAlloc(VDIDISK_DEFAULT_BUFFER_SIZE);
2774 if (pvBuf)
2775 {
2776 /* Move data. */
2777 for (;;)
2778 {
2779 unsigned cbToMove = (unsigned)RT_MIN(cbData, VDIDISK_DEFAULT_BUFFER_SIZE);
2780
2781 /* Read. */
2782 rc = RTFileSeek(pImage->File, offFile, RTFILE_SEEK_BEGIN, NULL);
2783 if (VBOX_FAILURE(rc))
2784 break;
2785 rc = RTFileRead(pImage->File, pvBuf, cbToMove, NULL);
2786 if (VBOX_FAILURE(rc))
2787 break;
2788
2789 /* Write. */
2790 rc = RTFileSeek(pImage->File, offFile + off, RTFILE_SEEK_BEGIN, NULL);
2791 if (VBOX_FAILURE(rc))
2792 break;
2793 rc = RTFileWrite(pImage->File, pvBuf, cbToMove, NULL);
2794 if (VBOX_FAILURE(rc))
2795 break;
2796
2797 if (pfnProgress)
2798 {
2799 c++;
2800 pfnProgress(NULL /* Achtung! pVM=NULL */,
2801 (c * 100) / cMoves,
2802 pvUser);
2803 /* Note: conversion is non breakable operation, skipping rc here. */
2804 }
2805
2806 cbData -= cbToMove;
2807 if (cbData == 0)
2808 break;
2809
2810 if (cbData > VDIDISK_DEFAULT_BUFFER_SIZE)
2811 offFile -= VDIDISK_DEFAULT_BUFFER_SIZE;
2812 else
2813 offFile = getImageDataOffset(&pImage->Header);
2814 }
2815
2816 /* Fill the beginning of file with zeroes to wipe out old headers etc. */
2817 if (VBOX_SUCCESS(rc))
2818 {
2819 Assert(offFile + off <= VDIDISK_DEFAULT_BUFFER_SIZE);
2820 rc = RTFileSeek(pImage->File, 0, RTFILE_SEEK_BEGIN, NULL);
2821 if (VBOX_SUCCESS(rc))
2822 {
2823 memset(pvBuf, 0, (unsigned)offFile + off);
2824 rc = RTFileWrite(pImage->File, pvBuf, (unsigned)offFile + off, NULL);
2825 }
2826 }
2827
2828 RTMemTmpFree(pvBuf);
2829 }
2830 else
2831 rc = VERR_NO_MEMORY;
2832
2833 if (VBOX_FAILURE(rc))
2834 goto l_conversion_failed;
2835 }
2836
2837 if (pfnProgress)
2838 {
2839 pfnProgress(NULL /* Achtung! pVM=NULL */, 100, pvUser);
2840 /* Note: conversion is non breakable operation, skipping rc here. */
2841 }
2842
2843 /* Data moved, now we need to save new pre header, header and blocks array. */
2844
2845 vdiInitPreHeader(&pImage->PreHeader);
2846 pImage->Header = Header;
2847
2848 /* Setup image parameters by header. */
2849 vdiSetupImageDesc(pImage);
2850
2851 /* Write pre-header. */
2852 rc = RTFileSeek(pImage->File, 0, RTFILE_SEEK_BEGIN, NULL);
2853 if (VBOX_FAILURE(rc))
2854 goto l_conversion_failed;
2855 rc = RTFileWrite(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
2856 if (VBOX_FAILURE(rc))
2857 goto l_conversion_failed;
2858
2859 /* Write header and blocks array. */
2860 rc = vdiUpdateBlocks(pImage);
2861
2862l_conversion_failed:
2863 vdiCloseImage(pImage);
2864
2865 LogFlow(("VDIConvertImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
2866 return rc;
2867}
2868
2869/**
2870 * Queries the image's UUID and parent UUIDs.
2871 *
2872 * @returns VBox status code.
2873 * @param pszFilename Name of the image file to operate on.
2874 * @param pUuid Where to store image UUID (can be NULL).
2875 * @param pModificationUuid Where to store modification UUID (can be NULL).
2876 * @param pParentUuuid Where to store parent UUID (can be NULL).
2877 * @param pParentModificationUuid Where to store parent modification UUID (can be NULL).
2878 */
2879IDER3DECL(int) VDIGetImageUUIDs(const char *pszFilename,
2880 PRTUUID pUuid, PRTUUID pModificationUuid,
2881 PRTUUID pParentUuid, PRTUUID pParentModificationUuid)
2882{
2883 LogFlow(("VDIGetImageUUIDs:\n"));
2884
2885 /* Check arguments. */
2886 if ( !pszFilename
2887 || *pszFilename == '\0')
2888 {
2889 AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
2890 return VERR_INVALID_PARAMETER;
2891 }
2892
2893 /*
2894 * Try open the specified image.
2895 */
2896 PVDIIMAGEDESC pImage;
2897 int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
2898 if (VBOX_FAILURE(rc))
2899 {
2900 Log(("VDIGetImageUUIDs: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
2901 return rc;
2902 }
2903
2904 /*
2905 * Query data.
2906 */
2907 if (pUuid)
2908 {
2909 PCRTUUID pTmpUuid = getImageCreationUUID(&pImage->Header);
2910 if (pTmpUuid)
2911 *pUuid = *pTmpUuid;
2912 else
2913 RTUuidClear(pUuid);
2914 }
2915 if (pModificationUuid)
2916 {
2917 PCRTUUID pTmpUuid = getImageModificationUUID(&pImage->Header);
2918 if (pTmpUuid)
2919 *pModificationUuid = *pTmpUuid;
2920 else
2921 RTUuidClear(pModificationUuid);
2922 }
2923 if (pParentUuid)
2924 {
2925 PCRTUUID pTmpUuid = getImageParentUUID(&pImage->Header);
2926 if (pTmpUuid)
2927 *pParentUuid = *pTmpUuid;
2928 else
2929 RTUuidClear(pParentUuid);
2930 }
2931 if (pParentModificationUuid)
2932 {
2933 PCRTUUID pTmpUuid = getImageParentModificationUUID(&pImage->Header);
2934 if (pTmpUuid)
2935 *pParentModificationUuid = *pTmpUuid;
2936 else
2937 RTUuidClear(pParentModificationUuid);
2938 }
2939
2940 /*
2941 * Close the image.
2942 */
2943 vdiCloseImage(pImage);
2944
2945 return VINF_SUCCESS;
2946}
2947
2948/**
2949 * Changes the image's UUID and parent UUIDs.
2950 *
2951 * @returns VBox status code.
2952 * @param pszFilename Name of the image file to operate on.
2953 * @param pUuid Optional parameter, new UUID of the image.
2954 * @param pModificationUuid Optional parameter, new modification UUID of the image.
2955 * @param pParentUuuid Optional parameter, new parent UUID of the image.
2956 * @param pParentModificationUuid Optional parameter, new parent modification UUID of the image.
2957 */
2958IDER3DECL(int) VDISetImageUUIDs(const char *pszFilename,
2959 PCRTUUID pUuid, PCRTUUID pModificationUuid,
2960 PCRTUUID pParentUuid, PCRTUUID pParentModificationUuid)
2961{
2962 LogFlow(("VDISetImageUUIDs:\n"));
2963
2964 /* Check arguments. */
2965 if ( !pszFilename
2966 || *pszFilename == '\0')
2967 {
2968 AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
2969 return VERR_INVALID_PARAMETER;
2970 }
2971
2972 PVDIIMAGEDESC pImage;
2973 int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
2974 if (VBOX_FAILURE(rc))
2975 {
2976 Log(("VDISetImageUUIDs: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
2977 return rc;
2978 }
2979 if (!pImage->fReadOnly)
2980 {
2981 /* we only support new images */
2982 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2983 {
2984 if (pUuid)
2985 pImage->Header.u.v1.uuidCreate = *pUuid;
2986
2987 if (pModificationUuid)
2988 pImage->Header.u.v1.uuidModify = *pModificationUuid;
2989
2990 if (pParentUuid)
2991 pImage->Header.u.v1.uuidLinkage = *pParentUuid;
2992
2993 if (pParentModificationUuid)
2994 pImage->Header.u.v1.uuidParentModify = *pParentModificationUuid;
2995
2996 /* write out new header */
2997 rc = vdiUpdateHeader(pImage);
2998 AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
2999 pImage->szFilename, rc));
3000 }
3001 else
3002 {
3003 Log(("VDISetImageUUIDs: Version is not supported!\n"));
3004 rc = VERR_VDI_UNSUPPORTED_VERSION;
3005 }
3006 }
3007 else
3008 {
3009 Log(("VDISetImageUUIDs: image \"%s\" is opened as read-only!\n", pszFilename));
3010 rc = VERR_VDI_IMAGE_READ_ONLY;
3011 }
3012
3013 vdiCloseImage(pImage);
3014 return rc;
3015}
3016
3017/**
3018 * Merges two images having a parent/child relationship (both directions).
3019 *
3020 * @returns VBox status code.
3021 * @param pszFilenameFrom Name of the image file to merge from.
3022 * @param pszFilenameTo Name of the image file to merge into.
3023 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
3024 * @param pvUser User argument for the progress callback.
3025 */
3026IDER3DECL(int) VDIMergeImage(const char *pszFilenameFrom, const char *pszFilenameTo,
3027 PFNVMPROGRESS pfnProgress, void *pvUser)
3028{
3029 LogFlow(("VDIMergeImage:\n"));
3030
3031 /* Check arguments. */
3032 if ( !pszFilenameFrom
3033 || *pszFilenameFrom == '\0'
3034 || !pszFilenameTo
3035 || *pszFilenameTo == '\0')
3036 {
3037 AssertMsgFailed(("Invalid arguments: pszFilenameFrom=%p, pszFilenameTo=%p\n", pszFilenameFrom, pszFilenameTo));
3038 return VERR_INVALID_PARAMETER;
3039 }
3040
3041 PVDIIMAGEDESC pImageFrom;
3042 int rc = vdiOpenImage(&pImageFrom, pszFilenameFrom, VDI_OPEN_FLAGS_READONLY, NULL);
3043 if (VBOX_FAILURE(rc))
3044 {
3045 Log(("VDIMergeImage: vdiOpenImage rc=%Vrc pstFilenameFrom=\"%s\"\n", rc, pszFilenameFrom));
3046 return rc;
3047 }
3048
3049 PVDIIMAGEDESC pImageTo;
3050 rc = vdiOpenImage(&pImageTo, pszFilenameTo, VDI_OPEN_FLAGS_NORMAL, NULL);
3051 if (VBOX_FAILURE(rc))
3052 {
3053 Log(("VDIMergeImage: vdiOpenImage rc=%Vrc pszFilenameTo=\"%s\"\n", rc, pszFilenameTo));
3054 vdiCloseImage(pImageFrom);
3055 return rc;
3056 }
3057 if (pImageTo->fReadOnly)
3058 {
3059 Log(("VDIMergeImage: image \"%s\" is opened as read-only!\n", pszFilenameTo));
3060 vdiCloseImage(pImageFrom);
3061 vdiCloseImage(pImageTo);
3062 return VERR_VDI_IMAGE_READ_ONLY;
3063 }
3064
3065 /*
3066 * when merging, we should not update the modification uuid of the target
3067 * image, because from the point of view of its children, it hasn't been
3068 * logically changed after the successful merge.
3069 */
3070 vdiDisableLastModifiedUpdate(pImageTo);
3071
3072 /*
3073 * Check in which direction we merge
3074 */
3075
3076 bool bParentToChild = false;
3077 if ( getImageParentUUID(&pImageFrom->Header)
3078 && !RTUuidCompare(getImageParentUUID(&pImageFrom->Header),
3079 getImageCreationUUID(&pImageTo->Header))
3080 && !RTUuidCompare(getImageParentModificationUUID(&pImageFrom->Header),
3081 getImageModificationUUID(&pImageTo->Header)))
3082 {
3083 /* we merge from a child to its parent */
3084 }
3085 else
3086 if ( getImageParentUUID(&pImageTo->Header)
3087 && !RTUuidCompare(getImageParentUUID(&pImageTo->Header),
3088 getImageCreationUUID(&pImageFrom->Header))
3089 && !RTUuidCompare(getImageParentModificationUUID(&pImageTo->Header),
3090 getImageModificationUUID(&pImageFrom->Header)))
3091 {
3092 /* we merge from a parent to its child */
3093 bParentToChild = true;
3094 }
3095 else
3096 {
3097 /* the images are not related, we can't merge! */
3098 Log(("VDIMergeImages: images do not have a parent/child or child/parent relationship!\n"));
3099 rc = VERR_VDI_IMAGES_UUID_MISMATCH;
3100 }
3101
3102 rc = vdiMergeImages(pImageFrom, pImageTo, bParentToChild, pfnProgress, pvUser);
3103
3104 if (pfnProgress)
3105 {
3106 pfnProgress(NULL /* Achtung! pVM=NULL */, 100, pvUser);
3107 /* Note: commiting is non breakable operation, skipping rc here. */
3108 }
3109
3110 /* cleanup */
3111 vdiCloseImage(pImageFrom);
3112 vdiCloseImage(pImageTo);
3113
3114 Log(("VDIMergeImage: done, returning with rc = %Vrc\n", rc));
3115 return rc;
3116}
3117
3118
3119/**
3120 * internal: initialize VDIDISK structure.
3121 */
3122static void vdiInitVDIDisk(PVDIDISK pDisk)
3123{
3124 Assert(pDisk);
3125 pDisk->u32Signature = VDIDISK_SIGNATURE;
3126 pDisk->cImages = 0;
3127 pDisk->pBase = NULL;
3128 pDisk->pLast = NULL;
3129 pDisk->cbBlock = VDI_IMAGE_DEFAULT_BLOCK_SIZE;
3130 pDisk->cbBuf = VDIDISK_DEFAULT_BUFFER_SIZE;
3131}
3132
3133/**
3134 * internal: add image structure to the end of images list.
3135 */
3136static void vdiAddImageToList(PVDIDISK pDisk, PVDIIMAGEDESC pImage)
3137{
3138 pImage->pPrev = NULL;
3139 pImage->pNext = NULL;
3140
3141 if (pDisk->pBase)
3142 {
3143 Assert(pDisk->cImages > 0);
3144 pImage->pPrev = pDisk->pLast;
3145 pDisk->pLast->pNext = pImage;
3146 pDisk->pLast = pImage;
3147 }
3148 else
3149 {
3150 Assert(pDisk->cImages == 0);
3151 pDisk->pBase = pImage;
3152 pDisk->pLast = pImage;
3153 }
3154
3155 pDisk->cImages++;
3156}
3157
3158/**
3159 * internal: remove image structure from the images list.
3160 */
3161static void vdiRemoveImageFromList(PVDIDISK pDisk, PVDIIMAGEDESC pImage)
3162{
3163 Assert(pDisk->cImages > 0);
3164
3165 if (pImage->pPrev)
3166 pImage->pPrev->pNext = pImage->pNext;
3167 else
3168 pDisk->pBase = pImage->pNext;
3169
3170 if (pImage->pNext)
3171 pImage->pNext->pPrev = pImage->pPrev;
3172 else
3173 pDisk->pLast = pImage->pPrev;
3174
3175 pImage->pPrev = NULL;
3176 pImage->pNext = NULL;
3177
3178 pDisk->cImages--;
3179}
3180
3181/**
3182 * Allocates and initializes VDI HDD container.
3183 *
3184 * @returns Pointer to newly created HDD container with no one opened image file.
3185 * @returns NULL on failure, typically out of memory.
3186 */
3187IDER3DECL(PVDIDISK) VDIDiskCreate(void)
3188{
3189 PVDIDISK pDisk = (PVDIDISK)RTMemAllocZ(sizeof(VDIDISK));
3190 if (pDisk)
3191 vdiInitVDIDisk(pDisk);
3192 LogFlow(("VDIDiskCreate: returns pDisk=%X\n", pDisk));
3193 return pDisk;
3194}
3195
3196/**
3197 * Destroys VDI HDD container. If container has opened image files they will be closed.
3198 *
3199 * @param pDisk Pointer to VDI HDD container.
3200 */
3201IDER3DECL(void) VDIDiskDestroy(PVDIDISK pDisk)
3202{
3203 LogFlow(("VDIDiskDestroy: pDisk=%X\n", pDisk));
3204 /* sanity check */
3205 Assert(pDisk);
3206 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3207
3208 if (pDisk)
3209 {
3210 VDIDiskCloseAllImages(pDisk);
3211 RTMemFree(pDisk);
3212 }
3213}
3214
3215/**
3216 * Get working buffer size of VDI HDD container.
3217 *
3218 * @returns Working buffer size in bytes.
3219 */
3220IDER3DECL(unsigned) VDIDiskGetBufferSize(PVDIDISK pDisk)
3221{
3222 /* sanity check */
3223 Assert(pDisk);
3224 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3225
3226 LogFlow(("VDIDiskGetBufferSize: returns %u\n", pDisk->cbBuf));
3227 return pDisk->cbBuf;
3228}
3229
3230/**
3231 * Get read/write mode of VDI HDD.
3232 *
3233 * @returns Disk ReadOnly status.
3234 * @returns true if no one VDI image is opened in HDD container.
3235 */
3236IDER3DECL(bool) VDIDiskIsReadOnly(PVDIDISK pDisk)
3237{
3238 /* sanity check */
3239 Assert(pDisk);
3240 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3241
3242 if (pDisk->pLast)
3243 {
3244 LogFlow(("VDIDiskIsReadOnly: returns %u\n", pDisk->pLast->fReadOnly));
3245 return pDisk->pLast->fReadOnly;
3246 }
3247
3248 AssertMsgFailed(("No one disk image is opened!\n"));
3249 return true;
3250}
3251
3252/**
3253 * Get disk size of VDI HDD container.
3254 *
3255 * @returns Virtual disk size in bytes.
3256 * @returns 0 if no one VDI image is opened in HDD container.
3257 */
3258IDER3DECL(uint64_t) VDIDiskGetSize(PVDIDISK pDisk)
3259{
3260 /* sanity check */
3261 Assert(pDisk);
3262 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3263
3264 if (pDisk->pBase)
3265 {
3266 LogFlow(("VDIDiskGetSize: returns %llu\n", getImageDiskSize(&pDisk->pBase->Header)));
3267 return getImageDiskSize(&pDisk->pBase->Header);
3268 }
3269
3270 AssertMsgFailed(("No one disk image is opened!\n"));
3271 return 0;
3272}
3273
3274/**
3275 * Get block size of VDI HDD container.
3276 *
3277 * @returns VDI image block size in bytes.
3278 * @returns 0 if no one VDI image is opened in HDD container.
3279 */
3280IDER3DECL(unsigned) VDIDiskGetBlockSize(PVDIDISK pDisk)
3281{
3282 /* sanity check */
3283 Assert(pDisk);
3284 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3285
3286 if (pDisk->pBase)
3287 {
3288 LogFlow(("VDIDiskGetBlockSize: returns %u\n", getImageBlockSize(&pDisk->pBase->Header)));
3289 return getImageBlockSize(&pDisk->pBase->Header);
3290 }
3291
3292 AssertMsgFailed(("No one disk image is opened!\n"));
3293 return 0;
3294}
3295
3296/**
3297 * Get virtual disk geometry stored in image file.
3298 *
3299 * @returns VBox status code.
3300 * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
3301 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry has been setted.
3302 * @param pDisk Pointer to VDI HDD container.
3303 * @param pcCylinders Where to store the number of cylinders. NULL is ok.
3304 * @param pcHeads Where to store the number of heads. NULL is ok.
3305 * @param pcSectors Where to store the number of sectors. NULL is ok.
3306 */
3307IDER3DECL(int) VDIDiskGetGeometry(PVDIDISK pDisk, unsigned *pcCylinders, unsigned *pcHeads, unsigned *pcSectors)
3308{
3309 /* sanity check */
3310 Assert(pDisk);
3311 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3312
3313 if (pDisk->pBase)
3314 {
3315 int rc = VINF_SUCCESS;
3316 PVDIDISKGEOMETRY pGeometry = getImageGeometry(&pDisk->pBase->Header);
3317 LogFlow(("VDIDiskGetGeometry: C/H/S = %u/%u/%u\n",
3318 pGeometry->cCylinders, pGeometry->cHeads, pGeometry->cSectors));
3319 if ( pGeometry->cCylinders > 0
3320 && pGeometry->cHeads > 0
3321 && pGeometry->cSectors > 0)
3322 {
3323 if (pcCylinders)
3324 *pcCylinders = pGeometry->cCylinders;
3325 if (pcHeads)
3326 *pcHeads = pGeometry->cHeads;
3327 if (pcSectors)
3328 *pcSectors = pGeometry->cSectors;
3329 }
3330 else
3331 rc = VERR_VDI_GEOMETRY_NOT_SET;
3332
3333 LogFlow(("VDIDiskGetGeometry: returns %Vrc\n", rc));
3334 return rc;
3335 }
3336
3337 AssertMsgFailed(("No one disk image is opened!\n"));
3338 return VERR_VDI_NOT_OPENED;
3339}
3340
3341/**
3342 * Store virtual disk geometry into base image file of HDD container.
3343 *
3344 * Note that in case of unrecoverable error all images of HDD container will be closed.
3345 *
3346 * @returns VBox status code.
3347 * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
3348 * @param pDisk Pointer to VDI HDD container.
3349 * @param cCylinders Number of cylinders.
3350 * @param cHeads Number of heads.
3351 * @param cSectors Number of sectors.
3352 */
3353IDER3DECL(int) VDIDiskSetGeometry(PVDIDISK pDisk, unsigned cCylinders, unsigned cHeads, unsigned cSectors)
3354{
3355 LogFlow(("VDIDiskSetGeometry: C/H/S = %u/%u/%u\n", cCylinders, cHeads, cSectors));
3356 /* sanity check */
3357 Assert(pDisk);
3358 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3359
3360 if (pDisk->pBase)
3361 {
3362 PVDIDISKGEOMETRY pGeometry = getImageGeometry(&pDisk->pBase->Header);
3363 pGeometry->cCylinders = cCylinders;
3364 pGeometry->cHeads = cHeads;
3365 pGeometry->cSectors = cSectors;
3366 pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
3367
3368 /* Update header information in base image file. */
3369 int rc = vdiUpdateReadOnlyHeader(pDisk->pBase);
3370 LogFlow(("VDIDiskSetGeometry: returns %Vrc\n", rc));
3371 return rc;
3372 }
3373
3374 AssertMsgFailed(("No one disk image is opened!\n"));
3375 return VERR_VDI_NOT_OPENED;
3376}
3377
3378/**
3379 * Get virtual disk translation mode stored in image file.
3380 *
3381 * @returns VBox status code.
3382 * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
3383 * @param pDisk Pointer to VDI HDD container.
3384 * @param penmTranslation Where to store the translation mode (see pdm.h).
3385 */
3386IDER3DECL(int) VDIDiskGetTranslation(PVDIDISK pDisk, PPDMBIOSTRANSLATION penmTranslation)
3387{
3388 /* sanity check */
3389 Assert(pDisk);
3390 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3391 Assert(penmTranslation);
3392
3393 if (pDisk->pBase)
3394 {
3395 *penmTranslation = getImageTranslation(&pDisk->pBase->Header);
3396 LogFlow(("VDIDiskGetTranslation: translation=%d\n", *penmTranslation));
3397 return VINF_SUCCESS;
3398 }
3399
3400 AssertMsgFailed(("No one disk image is opened!\n"));
3401 return VERR_VDI_NOT_OPENED;
3402}
3403
3404/**
3405 * Store virtual disk translation mode into base image file of HDD container.
3406 *
3407 * Note that in case of unrecoverable error all images of HDD container will be closed.
3408 *
3409 * @returns VBox status code.
3410 * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
3411 * @param pDisk Pointer to VDI HDD container.
3412 * @param enmTranslation Translation mode (see pdm.h).
3413 */
3414IDER3DECL(int) VDIDiskSetTranslation(PVDIDISK pDisk, PDMBIOSTRANSLATION enmTranslation)
3415{
3416 LogFlow(("VDIDiskSetTranslation: enmTranslation=%d\n", enmTranslation));
3417 /* sanity check */
3418 Assert(pDisk);
3419 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3420
3421 if (pDisk->pBase)
3422 {
3423 setImageTranslation(&pDisk->pBase->Header, enmTranslation);
3424
3425 /* Update header information in base image file. */
3426 int rc = vdiUpdateReadOnlyHeader(pDisk->pBase);
3427 LogFlow(("VDIDiskSetTranslation: returns %Vrc\n", rc));
3428 return rc;
3429 }
3430
3431 AssertMsgFailed(("No one disk image is opened!\n"));
3432 return VERR_VDI_NOT_OPENED;
3433}
3434
3435/**
3436 * Get number of opened images in HDD container.
3437 *
3438 * @returns Number of opened images for HDD container. 0 if no images is opened.
3439 * @param pDisk Pointer to VDI HDD container.
3440 */
3441IDER3DECL(int) VDIDiskGetImagesCount(PVDIDISK pDisk)
3442{
3443 /* sanity check */
3444 Assert(pDisk);
3445 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3446
3447 LogFlow(("VDIDiskGetImagesCount: returns %d\n", pDisk->cImages));
3448 return pDisk->cImages;
3449}
3450
3451static PVDIIMAGEDESC vdiGetImageByNumber(PVDIDISK pDisk, int nImage)
3452{
3453 PVDIIMAGEDESC pImage = pDisk->pBase;
3454 while (pImage && nImage)
3455 {
3456 pImage = pImage->pNext;
3457 nImage--;
3458 }
3459 return pImage;
3460}
3461
3462/**
3463 * Get version of opened image of HDD container.
3464 *
3465 * @returns VBox status code.
3466 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3467 * @param pDisk Pointer to VDI HDD container.
3468 * @param nImage Image number, counts from 0. 0 is always base image of container.
3469 * @param puVersion Where to store the image version.
3470 */
3471IDER3DECL(int) VDIDiskGetImageVersion(PVDIDISK pDisk, int nImage, unsigned *puVersion)
3472{
3473 /* sanity check */
3474 Assert(pDisk);
3475 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3476 Assert(puVersion);
3477
3478 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3479 Assert(pImage);
3480
3481 if (pImage)
3482 {
3483 *puVersion = pImage->PreHeader.u32Version;
3484 LogFlow(("VDIDiskGetImageVersion: returns %08X\n", pImage->PreHeader.u32Version));
3485 return VINF_SUCCESS;
3486 }
3487
3488 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3489 return VERR_VDI_IMAGE_NOT_FOUND;
3490}
3491
3492/**
3493 * Get filename of opened image of HDD container.
3494 *
3495 * @returns VBox status code.
3496 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3497 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
3498 * @param pDisk Pointer to VDI HDD container.
3499 * @param nImage Image number, counts from 0. 0 is always base image of container.
3500 * @param pszFilename Where to store the image file name.
3501 * @param cbFilename Size of buffer pszFilename points to.
3502 */
3503IDER3DECL(int) VDIDiskGetImageFilename(PVDIDISK pDisk, int nImage, char *pszFilename, unsigned cbFilename)
3504{
3505 /* sanity check */
3506 Assert(pDisk);
3507 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3508 Assert(pszFilename);
3509
3510 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3511 Assert(pImage);
3512
3513 if (pImage)
3514 {
3515 unsigned cb = strlen(pImage->szFilename);
3516 if (cb < cbFilename)
3517 {
3518 /* memcpy is much better than strncpy. */
3519 memcpy(pszFilename, pImage->szFilename, cb + 1);
3520 LogFlow(("VDIDiskGetImageFilename: returns VINF_SUCCESS, filename=\"%s\" nImage=%d\n",
3521 pszFilename, nImage));
3522 return VINF_SUCCESS;
3523 }
3524 else
3525 {
3526 AssertMsgFailed(("Out of buffer space, cbFilename=%d needed=%d\n", cbFilename, cb + 1));
3527 return VERR_BUFFER_OVERFLOW;
3528 }
3529 }
3530
3531 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3532 return VERR_VDI_IMAGE_NOT_FOUND;
3533}
3534
3535/**
3536 * Get the comment line of opened image of HDD container.
3537 *
3538 * @returns VBox status code.
3539 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3540 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
3541 * @param pDisk Pointer to VDI HDD container.
3542 * @param nImage Image number, counts from 0. 0 is always base image of container.
3543 * @param pszComment Where to store the comment string of image. NULL is ok.
3544 * @param cbComment The size of pszComment buffer. 0 is ok.
3545 */
3546IDER3DECL(int) VDIDiskGetImageComment(PVDIDISK pDisk, int nImage, char *pszComment, unsigned cbComment)
3547{
3548 /* sanity check */
3549 Assert(pDisk);
3550 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3551 Assert(pszComment);
3552
3553 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3554 Assert(pImage);
3555
3556 if (pImage)
3557 {
3558 char *pszTmp = getImageComment(&pImage->Header);
3559 unsigned cb = strlen(pszTmp);
3560 if (cb < cbComment)
3561 {
3562 /* memcpy is much better than strncpy. */
3563 memcpy(pszComment, pszTmp, cb + 1);
3564 LogFlow(("VDIDiskGetImageComment: returns VINF_SUCCESS, comment=\"%s\" nImage=%d\n",
3565 pszTmp, nImage));
3566 return VINF_SUCCESS;
3567 }
3568 else
3569 {
3570 AssertMsgFailed(("Out of buffer space, cbComment=%d needed=%d\n", cbComment, cb + 1));
3571 return VERR_BUFFER_OVERFLOW;
3572 }
3573 }
3574
3575 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3576 return VERR_VDI_IMAGE_NOT_FOUND;
3577}
3578
3579/**
3580 * Get type of opened image of HDD container.
3581 *
3582 * @returns VBox status code.
3583 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3584 * @param pDisk Pointer to VDI HDD container.
3585 * @param nImage Image number, counts from 0. 0 is always base image of container.
3586 * @param penmType Where to store the image type.
3587 */
3588IDER3DECL(int) VDIDiskGetImageType(PVDIDISK pDisk, int nImage, PVDIIMAGETYPE penmType)
3589{
3590 /* sanity check */
3591 Assert(pDisk);
3592 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3593 Assert(penmType);
3594
3595 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3596 Assert(pImage);
3597
3598 if (pImage)
3599 {
3600 *penmType = getImageType(&pImage->Header);
3601 LogFlow(("VDIDiskGetImageType: returns VINF_SUCCESS, type=%X nImage=%d\n",
3602 *penmType, nImage));
3603 return VINF_SUCCESS;
3604 }
3605
3606 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3607 return VERR_VDI_IMAGE_NOT_FOUND;
3608}
3609
3610/**
3611 * Get flags of opened image of HDD container.
3612 *
3613 * @returns VBox status code.
3614 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3615 * @param pDisk Pointer to VDI HDD container.
3616 * @param nImage Image number, counts from 0. 0 is always base image of container.
3617 * @param pfFlags Where to store the image flags.
3618 */
3619IDER3DECL(int) VDIDiskGetImageFlags(PVDIDISK pDisk, int nImage, unsigned *pfFlags)
3620{
3621 /* sanity check */
3622 Assert(pDisk);
3623 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3624 Assert(pfFlags);
3625
3626 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3627 Assert(pImage);
3628
3629 if (pImage)
3630 {
3631 *pfFlags = getImageFlags(&pImage->Header);
3632 LogFlow(("VDIDiskGetImageFlags: returns VINF_SUCCESS, flags=%08X nImage=%d\n",
3633 *pfFlags, nImage));
3634 return VINF_SUCCESS;
3635 }
3636
3637 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3638 return VERR_VDI_IMAGE_NOT_FOUND;
3639}
3640
3641/**
3642 * Get Uuid of opened image of HDD container.
3643 *
3644 * @returns VBox status code.
3645 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3646 * @param pDisk Pointer to VDI HDD container.
3647 * @param nImage Image number, counts from 0. 0 is always base image of container.
3648 * @param pUuid Where to store the image creation uuid.
3649 */
3650IDER3DECL(int) VDIDiskGetImageUuid(PVDIDISK pDisk, int nImage, PRTUUID pUuid)
3651{
3652 /* sanity check */
3653 Assert(pDisk);
3654 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3655 Assert(pUuid);
3656
3657 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3658 Assert(pImage);
3659
3660 if (pImage)
3661 {
3662 *pUuid = *getImageCreationUUID(&pImage->Header);
3663 LogFlow(("VDIDiskGetImageUuid: returns VINF_SUCCESS, uuid={%Vuuid} nImage=%d\n",
3664 pUuid, nImage));
3665 return VINF_SUCCESS;
3666 }
3667
3668 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3669 return VERR_VDI_IMAGE_NOT_FOUND;
3670}
3671
3672/**
3673 * Get last modification Uuid of opened image of HDD container.
3674 *
3675 * @returns VBox status code.
3676 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3677 * @param pDisk Pointer to VDI HDD container.
3678 * @param nImage Image number, counts from 0. 0 is always base image of container.
3679 * @param pUuid Where to store the image modification uuid.
3680 */
3681IDER3DECL(int) VDIDiskGetImageModificationUuid(PVDIDISK pDisk, int nImage, PRTUUID pUuid)
3682{
3683 /* sanity check */
3684 Assert(pDisk);
3685 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3686 Assert(pUuid);
3687
3688 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3689 Assert(pImage);
3690
3691 if (pImage)
3692 {
3693 *pUuid = *getImageModificationUUID(&pImage->Header);
3694 LogFlow(("VDIDiskGetImageModificationUuid: returns VINF_SUCCESS, uuid={%Vuuid} nImage=%d\n",
3695 pUuid, nImage));
3696 return VINF_SUCCESS;
3697 }
3698
3699 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3700 return VERR_VDI_IMAGE_NOT_FOUND;
3701}
3702
3703/**
3704 * Get Uuid of opened image's parent image.
3705 *
3706 * @returns VBox status code.
3707 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3708 * @param pDisk Pointer to VDI HDD container.
3709 * @param nImage Image number, counts from 0. 0 is always base image of the container.
3710 * @param pUuid Where to store the image creation uuid.
3711 */
3712IDER3DECL(int) VDIDiskGetParentImageUuid(PVDIDISK pDisk, int nImage, PRTUUID pUuid)
3713{
3714 /* sanity check */
3715 Assert(pDisk);
3716 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3717 Assert(pUuid);
3718
3719 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3720 if (pImage)
3721 {
3722 *pUuid = *getImageParentUUID(&pImage->Header);
3723 LogFlow(("VDIDiskGetParentImageUuid: returns VINF_SUCCESS, *pUuid={%Vuuid} nImage=%d\n",
3724 pUuid, nImage));
3725 return VINF_SUCCESS;
3726 }
3727
3728 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3729 return VERR_VDI_IMAGE_NOT_FOUND;
3730}
3731
3732/**
3733 * internal: Relock image as read/write or read-only.
3734 */
3735static int vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly)
3736{
3737 Assert(pImage);
3738
3739 if ( !fReadOnly
3740 && pImage->fOpen & VDI_OPEN_FLAGS_READONLY)
3741 {
3742 /* Can't switch read-only opened image to read-write mode. */
3743 Log(("vdiChangeImageMode: can't switch r/o image to r/w mode, filename=\"%s\" fOpen=%X\n",
3744 pImage->szFilename, pImage->fOpen));
3745 return VERR_VDI_IMAGE_READ_ONLY;
3746 }
3747
3748 /* Flush last image changes if was r/w mode. */
3749 vdiFlushImage(pImage);
3750
3751 /* Change image locking. */
3752 uint64_t cbLock = pImage->offStartData
3753 + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
3754 int rc = RTFileChangeLock(pImage->File,
3755 (fReadOnly) ?
3756 RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY :
3757 RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY,
3758 0,
3759 cbLock);
3760 if (VBOX_SUCCESS(rc))
3761 {
3762 pImage->fReadOnly = fReadOnly;
3763 Log(("vdiChangeImageMode: Image \"%s\" mode changed to %s\n",
3764 pImage->szFilename, (pImage->fReadOnly) ? "read-only" : "read/write"));
3765 return VINF_SUCCESS;
3766 }
3767
3768 /* Check for the most bad error in the world. Damn! It must never happens in real life! */
3769 if (rc == VERR_FILE_LOCK_LOST)
3770 {
3771 /* And what we can do now?! */
3772 AssertMsgFailed(("Image lock has been lost for file=\"%s\"", pImage->szFilename));
3773 Log(("vdiChangeImageMode: image lock has been lost for file=\"%s\", blocking on r/o lock wait",
3774 pImage->szFilename));
3775
3776 /* Try to obtain read lock in blocking mode. Maybe it's a very bad method. */
3777 rc = RTFileLock(pImage->File, RTFILE_LOCK_READ | RTFILE_LOCK_WAIT, 0, cbLock);
3778 AssertReleaseRC(rc);
3779
3780 pImage->fReadOnly = false;
3781 if (pImage->fReadOnly != fReadOnly)
3782 rc = VERR_FILE_LOCK_VIOLATION;
3783 }
3784
3785 Log(("vdiChangeImageMode: Image \"%s\" mode change failed with rc=%Vrc, mode is %s\n",
3786 pImage->szFilename, rc, (pImage->fReadOnly) ? "read-only" : "read/write"));
3787
3788 return rc;
3789}
3790
3791/**
3792 * internal: try to save header in image file even if image is in read-only mode.
3793 */
3794static int vdiUpdateReadOnlyHeader(PVDIIMAGEDESC pImage)
3795{
3796 int rc = VINF_SUCCESS;
3797
3798 if (pImage->fReadOnly)
3799 {
3800 rc = vdiChangeImageMode(pImage, false);
3801 if (VBOX_SUCCESS(rc))
3802 {
3803 vdiFlushImage(pImage);
3804 rc = vdiChangeImageMode(pImage, true);
3805 AssertReleaseRC(rc);
3806 }
3807 }
3808 else
3809 vdiFlushImage(pImage);
3810
3811 return rc;
3812}
3813
3814/**
3815 * Opens an image file.
3816 *
3817 * The first opened image file in a HDD container must have a base image type,
3818 * others (next opened images) must be a differencing or undo images.
3819 * Linkage is checked for differencing image to be in consistence with the previously opened image.
3820 * When a next differencing image is opened and the last image was opened in read/write access
3821 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
3822 * other processes to use images in read-only mode too.
3823 *
3824 * Note that the image can be opened in read-only mode if a read/write open is not possible.
3825 * Use VDIDiskIsReadOnly to check open mode.
3826 *
3827 * @returns VBox status code.
3828 * @param pDisk Pointer to VDI HDD container.
3829 * @param pszFilename Name of the image file to open.
3830 * @param fOpen Image file open mode, see VDI_OPEN_FLAGS_* constants.
3831 */
3832IDER3DECL(int) VDIDiskOpenImage(PVDIDISK pDisk, const char *pszFilename, unsigned fOpen)
3833{
3834 /* sanity check */
3835 Assert(pDisk);
3836 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3837
3838 /* Check arguments. */
3839 if ( !pszFilename
3840 || *pszFilename == '\0'
3841 || (fOpen & ~VDI_OPEN_FLAGS_MASK))
3842 {
3843 AssertMsgFailed(("Invalid arguments: pszFilename=%p fOpen=%x\n", pszFilename, fOpen));
3844 return VERR_INVALID_PARAMETER;
3845 }
3846 LogFlow(("VDIDiskOpenImage: pszFilename=\"%s\" fOpen=%X\n", pszFilename, fOpen));
3847
3848 PVDIIMAGEDESC pImage;
3849 int rc = vdiOpenImage(&pImage, pszFilename, fOpen, pDisk->pLast);
3850 if (VBOX_SUCCESS(rc))
3851 {
3852 if (pDisk->pLast)
3853 {
3854 /* Opening differencing image. */
3855 if (!pDisk->pLast->fReadOnly)
3856 {
3857 /*
3858 * Previous image is opened in read/write mode -> switch it into read-only.
3859 */
3860 rc = vdiChangeImageMode(pDisk->pLast, true);
3861 }
3862 }
3863 else
3864 {
3865 /* Opening base image, check its type. */
3866 if ( getImageType(&pImage->Header) != VDI_IMAGE_TYPE_NORMAL
3867 && getImageType(&pImage->Header) != VDI_IMAGE_TYPE_FIXED)
3868 {
3869 rc = VERR_VDI_INVALID_TYPE;
3870 }
3871 }
3872
3873 if (VBOX_SUCCESS(rc))
3874 vdiAddImageToList(pDisk, pImage);
3875 else
3876 vdiCloseImage(pImage);
3877 }
3878
3879 LogFlow(("VDIDiskOpenImage: returns %Vrc\n", rc));
3880 return rc;
3881}
3882
3883/**
3884 * Closes the last opened image file in the HDD container. Leaves all changes inside it.
3885 * If previous image file was opened in read-only mode (that is normal) and closing image
3886 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
3887 * will be reopened in read/write mode.
3888 *
3889 * @param pDisk Pointer to VDI HDD container.
3890 */
3891IDER3DECL(void) VDIDiskCloseImage(PVDIDISK pDisk)
3892{
3893 /* sanity check */
3894 Assert(pDisk);
3895 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3896
3897 PVDIIMAGEDESC pImage = pDisk->pLast;
3898 if (pImage)
3899 {
3900 LogFlow(("VDIDiskCloseImage: closing image \"%s\"\n", pImage->szFilename));
3901
3902 bool fWasReadOnly = pImage->fReadOnly;
3903 vdiRemoveImageFromList(pDisk, pImage);
3904 vdiCloseImage(pImage);
3905
3906 if ( !fWasReadOnly
3907 && pDisk->pLast
3908 && pDisk->pLast->fReadOnly
3909 && !(pDisk->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
3910 {
3911 /*
3912 * Closed image was opened in read/write mode, previous image was opened
3913 * in read-only mode, try to switch it into read/write.
3914 */
3915 int rc = vdiChangeImageMode(pDisk->pLast, false);
3916 NOREF(rc); /* gcc still hates unused variables... */
3917 }
3918
3919 return;
3920 }
3921 AssertMsgFailed(("No images to close\n"));
3922}
3923
3924/**
3925 * Closes all opened image files in HDD container.
3926 *
3927 * @param pDisk Pointer to VDI HDD container.
3928 */
3929IDER3DECL(void) VDIDiskCloseAllImages(PVDIDISK pDisk)
3930{
3931 LogFlow(("VDIDiskCloseAllImages:\n"));
3932 /* sanity check */
3933 Assert(pDisk);
3934 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3935
3936 PVDIIMAGEDESC pImage = pDisk->pLast;
3937 while (pImage)
3938 {
3939 PVDIIMAGEDESC pPrev = pImage->pPrev;
3940 vdiRemoveImageFromList(pDisk, pImage);
3941 vdiCloseImage(pImage);
3942 pImage = pPrev;
3943 }
3944 Assert(pDisk->pLast == NULL);
3945}
3946
3947/**
3948 * Commits last opened differencing/undo image file of HDD container to previous one.
3949 * If previous image file was opened in read-only mode (that must be always so) it is reopened
3950 * as read/write to do commit operation.
3951 * After successfull commit the previous image file again reopened in read-only mode, last opened
3952 * image file is cleared of data and remains open and active in HDD container.
3953 * If you want to delete image after commit you must do it manually by VDIDiskCloseImage and
3954 * VDIDeleteImage calls.
3955 *
3956 * Note that in case of unrecoverable error all images of HDD container will be closed.
3957 *
3958 * @returns VBox status code.
3959 * @param pDisk Pointer to VDI HDD container.
3960 * @param pfnProgress Progress callback. Optional.
3961 * @param pvUser User argument for the progress callback.
3962 */
3963IDER3DECL(int) VDIDiskCommitLastDiff(PVDIDISK pDisk, PFNVMPROGRESS pfnProgress, void *pvUser)
3964{
3965 LogFlow(("VDIDiskCommitLastDiff:\n"));
3966 /* sanity check */
3967 Assert(pDisk);
3968 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3969
3970 int rc = VINF_SUCCESS;
3971 PVDIIMAGEDESC pImage = pDisk->pLast;
3972 if (!pImage)
3973 {
3974 AssertMsgFailed(("No one disk image is opened!\n"));
3975 return VERR_VDI_NOT_OPENED;
3976 }
3977
3978 if (pImage->fReadOnly)
3979 {
3980 AssertMsgFailed(("Image \"%s\" is read-only!\n", pImage->szFilename));
3981 return VERR_VDI_IMAGE_READ_ONLY;
3982 }
3983
3984 if (!pImage->pPrev)
3985 {
3986 AssertMsgFailed(("No images to commit to!\n"));
3987 return VERR_VDI_NO_DIFF_IMAGES;
3988 }
3989
3990 bool fWasReadOnly = pImage->pPrev->fReadOnly;
3991 if (fWasReadOnly)
3992 {
3993 /* Change previous image mode to r/w. */
3994 rc = vdiChangeImageMode(pImage->pPrev, false);
3995 if (VBOX_FAILURE(rc))
3996 {
3997 Log(("VDIDiskCommitLastDiff: can't switch previous image into r/w mode, rc=%Vrc\n", rc));
3998 return rc;
3999 }
4000 }
4001
4002 rc = vdiCommitToImage(pDisk, pImage->pPrev, pfnProgress, pvUser);
4003 if (VBOX_SUCCESS(rc) && fWasReadOnly)
4004 {
4005 /* Change previous image mode back to r/o. */
4006 rc = vdiChangeImageMode(pImage->pPrev, true);
4007 }
4008
4009 if (VBOX_FAILURE(rc))
4010 {
4011 /* Failed! Close all images, can't work with VHDD at all. */
4012 VDIDiskCloseAllImages(pDisk);
4013 AssertMsgFailed(("Fatal: commit failed, rc=%Vrc\n", rc));
4014 }
4015
4016 return rc;
4017}
4018
4019/**
4020 * Creates and opens a new differencing image file in HDD container.
4021 * See comments for VDIDiskOpenImage function about differencing images.
4022 *
4023 * @returns VBox status code.
4024 * @param pDisk Pointer to VDI HDD container.
4025 * @param pszFilename Name of the image file to create and open.
4026 * @param pszComment Pointer to image comment. NULL is ok.
4027 * @param pfnProgress Progress callback. Optional.
4028 * @param pvUser User argument for the progress callback.
4029 */
4030IDER3DECL(int) VDIDiskCreateOpenDifferenceImage(PVDIDISK pDisk, const char *pszFilename, const char *pszComment,
4031 PFNVMPROGRESS pfnProgress, void *pvUser)
4032{
4033 LogFlow(("VDIDiskCreateOpenDifferenceImage:\n"));
4034 /* sanity check */
4035 Assert(pDisk);
4036 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4037 Assert(pszFilename);
4038
4039 if (!pDisk->pLast)
4040 {
4041 AssertMsgFailed(("No one disk image is opened!\n"));
4042 return VERR_VDI_NOT_OPENED;
4043 }
4044
4045 /* Flush last parent image changes if possible. */
4046 vdiFlushImage(pDisk->pLast);
4047
4048 int rc = vdiCreateImage(pszFilename,
4049 VDI_IMAGE_TYPE_DIFF,
4050 VDI_IMAGE_FLAGS_DEFAULT,
4051 getImageDiskSize(&pDisk->pLast->Header),
4052 pszComment,
4053 pDisk->pLast,
4054 pfnProgress, pvUser);
4055 if (VBOX_SUCCESS(rc))
4056 {
4057 rc = VDIDiskOpenImage(pDisk, pszFilename, VDI_OPEN_FLAGS_NORMAL);
4058 if (VBOX_FAILURE(rc))
4059 VDIDeleteImage(pszFilename);
4060 }
4061 LogFlow(("VDIDiskCreateOpenDifferenceImage: returns %Vrc, filename=\"%s\"\n", rc, pszFilename));
4062 return rc;
4063}
4064
4065/**
4066 * internal: debug image dump.
4067 */
4068static void vdiDumpImage(PVDIIMAGEDESC pImage)
4069{
4070 RTLogPrintf("Dumping VDI image \"%s\" mode=%s fOpen=%X File=%08X\n",
4071 pImage->szFilename,
4072 (pImage->fReadOnly) ? "r/o" : "r/w",
4073 pImage->fOpen,
4074 pImage->File);
4075 RTLogPrintf("Header: Version=%08X Type=%X Flags=%X Size=%llu\n",
4076 pImage->PreHeader.u32Version,
4077 getImageType(&pImage->Header),
4078 getImageFlags(&pImage->Header),
4079 getImageDiskSize(&pImage->Header));
4080 RTLogPrintf("Header: cbBlock=%u cbBlockExtra=%u cBlocks=%u cBlocksAllocated=%u\n",
4081 getImageBlockSize(&pImage->Header),
4082 getImageExtraBlockSize(&pImage->Header),
4083 getImageBlocks(&pImage->Header),
4084 getImageBlocksAllocated(&pImage->Header));
4085 RTLogPrintf("Header: offBlocks=%u offData=%u\n",
4086 getImageBlocksOffset(&pImage->Header),
4087 getImageDataOffset(&pImage->Header));
4088 PVDIDISKGEOMETRY pg = getImageGeometry(&pImage->Header);
4089 RTLogPrintf("Header->Geometry: C/H/S=%u/%u/%u cbSector=%u Mode=%u\n",
4090 pg->cCylinders, pg->cHeads, pg->cSectors, pg->cbSector,
4091 getImageTranslation(&pImage->Header));
4092 RTLogPrintf("Header: uuidCreation={%Vuuid}\n", getImageCreationUUID(&pImage->Header));
4093 RTLogPrintf("Header: uuidModification={%Vuuid}\n", getImageModificationUUID(&pImage->Header));
4094 RTLogPrintf("Header: uuidParent={%Vuuid}\n", getImageParentUUID(&pImage->Header));
4095 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) >= 1)
4096 RTLogPrintf("Header: uuidParentModification={%Vuuid}\n", getImageParentModificationUUID(&pImage->Header));
4097 RTLogPrintf("Image: fFlags=%08X offStartBlocks=%u offStartData=%u\n",
4098 pImage->fFlags, pImage->offStartBlocks, pImage->offStartData);
4099 RTLogPrintf("Image: uBlockMask=%08X uShiftIndex2Offset=%u uShiftOffset2Index=%u offStartBlockData=%u\n",
4100 pImage->uBlockMask,
4101 pImage->uShiftIndex2Offset,
4102 pImage->uShiftOffset2Index,
4103 pImage->offStartBlockData);
4104}
4105
4106/**
4107 * Debug helper - dumps all opened images of HDD container into the log file.
4108 *
4109 * @param pDisk Pointer to VDI HDD container.
4110 */
4111IDER3DECL(void) VDIDiskDumpImages(PVDIDISK pDisk)
4112{
4113 /* sanity check */
4114 Assert(pDisk);
4115 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
4116
4117 RTLogPrintf("--- Dumping VDI Disk, Images=%u\n", pDisk->cImages);
4118 for (PVDIIMAGEDESC pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
4119 vdiDumpImage(pImage);
4120}
4121
4122
4123/*******************************************************************************
4124* PDM interface *
4125*******************************************************************************/
4126
4127/**
4128 * Construct a VBox HDD media driver instance.
4129 *
4130 * @returns VBox status.
4131 * @param pDrvIns The driver instance data.
4132 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
4133 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
4134 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
4135 * iInstance it's expected to be used a bit in this function.
4136 */
4137static DECLCALLBACK(int) vdiConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
4138{
4139 LogFlow(("vdiConstruct:\n"));
4140 PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
4141
4142 /*
4143 * Init the static parts.
4144 */
4145 pDrvIns->IBase.pfnQueryInterface = vdiQueryInterface;
4146 pData->pDrvIns = pDrvIns;
4147
4148 vdiInitVDIDisk(pData);
4149
4150 /* IMedia */
4151 pData->IMedia.pfnRead = vdiRead;
4152 pData->IMedia.pfnWrite = vdiWrite;
4153 pData->IMedia.pfnFlush = vdiFlush;
4154 pData->IMedia.pfnGetSize = vdiGetSize;
4155 pData->IMedia.pfnGetUuid = vdiGetUuid;
4156 pData->IMedia.pfnIsReadOnly = vdiIsReadOnly;
4157 pData->IMedia.pfnBiosGetGeometry = vdiBiosGetGeometry;
4158 pData->IMedia.pfnBiosSetGeometry = vdiBiosSetGeometry;
4159 pData->IMedia.pfnBiosGetTranslation = vdiBiosGetTranslation;
4160 pData->IMedia.pfnBiosSetTranslation = vdiBiosSetTranslation;
4161
4162#if 1 /** @todo someone review this! it's a shot in the dark from my side. */
4163 /*
4164 * Validate configuration and find the great to the level of umpteen grandparent.
4165 * The parents are found in the 'Parent' subtree, so it's sorta up side down
4166 * from the image dependency tree.
4167 */
4168 unsigned iLevel = 0;
4169 PCFGMNODE pCurNode = pCfgHandle;
4170 for (;;)
4171 {
4172 if (!CFGMR3AreValuesValid(pCfgHandle, "Path\0ReadOnly\0"))
4173 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
4174
4175 PCFGMNODE pParent = CFGMR3GetChild(pCurNode, "Parent");
4176 if (!pParent)
4177 break;
4178 pCurNode = pParent;
4179 iLevel++;
4180 }
4181
4182 /*
4183 * Open the images.
4184 */
4185 int rc = VINF_SUCCESS;
4186 while (pCurNode && VBOX_SUCCESS(rc))
4187 {
4188 /*
4189 * Read the image configuration.
4190 */
4191 char *pszName;
4192 int rc = CFGMR3QueryStringAlloc(pCurNode, "Path", &pszName);
4193 if (VBOX_FAILURE(rc))
4194 {
4195 AssertMsgFailed(("Configuration error: %d - query for \"Path\" string return %Vrc.\n", iLevel, rc));
4196 return rc;
4197 }
4198
4199 bool fReadOnly;
4200 rc = CFGMR3QueryBool(pCfgHandle, "ReadOnly", &fReadOnly);
4201 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
4202 fReadOnly = false;
4203 else if (VBOX_FAILURE(rc))
4204 {
4205 AssertMsgFailed(("Configuration error: %d - query for \"ReadOnly\" boolean return %Vrc.\n", iLevel, rc));
4206 MMR3HeapFree(pszName);
4207 return rc;
4208 }
4209
4210 /*
4211 * Open the image.
4212 */
4213 rc = VDIDiskOpenImage(pData, pszName, !fReadOnly ? VDI_OPEN_FLAGS_NORMAL : VDI_OPEN_FLAGS_READONLY);
4214 if (VBOX_SUCCESS(rc))
4215 Log(("vdiConstruct: %d - Opened '%s' in %s mode\n",
4216 iLevel, pszName, VDIDiskIsReadOnly(pData) ? "read-only" : "read-write"));
4217 else
4218 AssertMsgFailed(("Failed to open image '%s' rc=%Vrc\n", pszName, rc));
4219 MMR3HeapFree(pszName);
4220
4221 /* next */
4222 iLevel--;
4223 pCurNode = CFGMR3GetParent(pCurNode);
4224 }
4225
4226 /* On failure, vdiDestruct will be called, so no need to clean up here. */
4227
4228#else /* old */
4229 /*
4230 * Validate and read top level configuration.
4231 */
4232 char *pszName;
4233 int rc = CFGMR3QueryStringAlloc(pCfgHandle, "Path", &pszName);
4234 if (VBOX_FAILURE(rc))
4235 {
4236 AssertMsgFailed(("Configuration error: query for \"Path\" string return %Vrc.\n", rc));
4237 return rc;
4238 }
4239
4240 /** True if the media is readonly. */
4241 bool fReadOnly;
4242 rc = CFGMR3QueryBool(pCfgHandle, "ReadOnly", &fReadOnly);
4243 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
4244 fReadOnly = false;
4245 else if (VBOX_FAILURE(rc))
4246 {
4247 AssertMsgFailed(("Configuration error: query for \"ReadOnly\" boolean return %Vrc.\n", rc));
4248 MMR3HeapFree(pszName);
4249 return rc;
4250 }
4251
4252 /*
4253 * Open the image.
4254 */
4255 rc = VDIDiskOpenImage(pData, pszName, !fReadOnly ? VDI_OPEN_FLAGS_NORMAL : VDI_OPEN_FLAGS_READONLY);
4256 if (VBOX_SUCCESS(rc))
4257 Log(("vdiConstruct: Opened '%s' in %s mode\n", pszName, VDIDiskIsReadOnly(pData) ? "read-only" : "read-write"));
4258 else
4259 AssertMsgFailed(("Failed to open image '%s' rc=%Vrc\n", pszName, rc));
4260
4261 MMR3HeapFree(pszName);
4262 pszName = NULL;
4263#endif
4264
4265 return rc;
4266}
4267
4268/**
4269 * Destruct a driver instance.
4270 *
4271 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
4272 * resources can be freed correctly.
4273 *
4274 * @param pDrvIns The driver instance data.
4275 */
4276static DECLCALLBACK(void) vdiDestruct(PPDMDRVINS pDrvIns)
4277{
4278 LogFlow(("vdiDestruct:\n"));
4279 PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
4280 VDIDiskCloseAllImages(pData);
4281}
4282
4283/**
4284 * When the VM has been suspended we'll change the image mode to read-only
4285 * so that main and others can read the VDIs. This is important when
4286 * saving state and so forth.
4287 *
4288 * @param pDrvIns The driver instance data.
4289 */
4290static DECLCALLBACK(void) vdiSuspend(PPDMDRVINS pDrvIns)
4291{
4292 LogFlow(("vdiSuspend:\n"));
4293#if 1 // #ifdef DEBUG_dmik
4294 PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
4295 if (!(pData->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
4296 {
4297 int rc = vdiChangeImageMode(pData->pLast, true);
4298 AssertRC(rc);
4299 }
4300#endif
4301}
4302
4303/**
4304 * Before the VM resumes we'll have to undo the read-only mode change
4305 * done in vdiSuspend.
4306 *
4307 * @param pDrvIns The driver instance data.
4308 */
4309static DECLCALLBACK(void) vdiResume(PPDMDRVINS pDrvIns)
4310{
4311 LogFlow(("vdiSuspend:\n"));
4312#if 1 //#ifdef DEBUG_dmik
4313 PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
4314 if (!(pData->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
4315 {
4316 int rc = vdiChangeImageMode(pData->pLast, false);
4317 AssertRC(rc);
4318 }
4319#endif
4320}
4321
4322
4323/** @copydoc PDMIMEDIA::pfnGetSize */
4324static DECLCALLBACK(uint64_t) vdiGetSize(PPDMIMEDIA pInterface)
4325{
4326 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
4327 uint64_t cb = VDIDiskGetSize(pData);
4328 LogFlow(("vdiGetSize: returns %#llx (%llu)\n", cb, cb));
4329 return cb;
4330}
4331
4332/**
4333 * Get stored media geometry - BIOS property.
4334 *
4335 * @see PDMIMEDIA::pfnBiosGetGeometry for details.
4336 */
4337static DECLCALLBACK(int) vdiBiosGetGeometry(PPDMIMEDIA pInterface, uint32_t *pcCylinders, uint32_t *pcHeads, uint32_t *pcSectors)
4338{
4339 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
4340 int rc = VDIDiskGetGeometry(pData, pcCylinders, pcHeads, pcSectors);
4341 if (VBOX_SUCCESS(rc))
4342 {
4343 LogFlow(("vdiBiosGetGeometry: returns VINF_SUCCESS\n"));
4344 return VINF_SUCCESS;
4345 }
4346 Log(("vdiBiosGetGeometry: The Bios geometry data was not available.\n"));
4347 return VERR_PDM_GEOMETRY_NOT_SET;
4348}
4349
4350/**
4351 * Set stored media geometry - BIOS property.
4352 *
4353 * @see PDMIMEDIA::pfnBiosSetGeometry for details.
4354 */
4355static DECLCALLBACK(int) vdiBiosSetGeometry(PPDMIMEDIA pInterface, uint32_t cCylinders, uint32_t cHeads, uint32_t cSectors)
4356{
4357 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
4358 int rc = VDIDiskSetGeometry(pData, cCylinders, cHeads, cSectors);
4359 LogFlow(("vdiBiosSetGeometry: returns %Vrc (%d,%d,%d)\n", rc, cCylinders, cHeads, cSectors));
4360 return rc;
4361}
4362
4363/**
4364 * Read bits.
4365 *
4366 * @see PDMIMEDIA::pfnRead for details.
4367 */
4368static DECLCALLBACK(int) vdiRead(PPDMIMEDIA pInterface, uint64_t off, void *pvBuf, size_t cbRead)
4369{
4370 LogFlow(("vdiRead: off=%#llx pvBuf=%p cbRead=%d\n", off, pvBuf, cbRead));
4371 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
4372 int rc = VDIDiskRead(pData, off, pvBuf, cbRead);
4373 if (VBOX_SUCCESS(rc))
4374 Log2(("vdiRead: off=%#llx pvBuf=%p cbRead=%d\n"
4375 "%.*Vhxd\n",
4376 off, pvBuf, cbRead, cbRead, pvBuf));
4377 LogFlow(("vdiRead: returns %Vrc\n", rc));
4378 return rc;
4379}
4380
4381/**
4382 * Write bits.
4383 *
4384 * @see PDMIMEDIA::pfnWrite for details.
4385 */
4386static DECLCALLBACK(int) vdiWrite(PPDMIMEDIA pInterface, uint64_t off, const void *pvBuf, size_t cbWrite)
4387{
4388 LogFlow(("vdiWrite: off=%#llx pvBuf=%p cbWrite=%d\n", off, pvBuf, cbWrite));
4389 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
4390 Log2(("vdiWrite: off=%#llx pvBuf=%p cbWrite=%d\n"
4391 "%.*Vhxd\n",
4392 off, pvBuf, cbWrite, cbWrite, pvBuf));
4393 int rc = VDIDiskWrite(pData, off, pvBuf, cbWrite);
4394 LogFlow(("vdiWrite: returns %Vrc\n", rc));
4395 return rc;
4396}
4397
4398/**
4399 * Flush bits to media.
4400 *
4401 * @see PDMIMEDIA::pfnFlush for details.
4402 */
4403static DECLCALLBACK(int) vdiFlush(PPDMIMEDIA pInterface)
4404{
4405 LogFlow(("vdiFlush:\n"));
4406 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
4407 vdiFlushImage(pData->pLast);
4408 int rc = VINF_SUCCESS;
4409 LogFlow(("vdiFlush: returns %Vrc\n", rc));
4410 return rc;
4411}
4412
4413/** @copydoc PDMIMEDIA::pfnGetUuid */
4414static DECLCALLBACK(int) vdiGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
4415{
4416 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
4417 int rc = VDIDiskGetImageUuid(pData, 0, pUuid);
4418 LogFlow(("vdiGetUuid: returns %Vrc ({%Vuuid})\n", rc, pUuid));
4419 return rc;
4420}
4421
4422/** @copydoc PDMIMEDIA::pfnIsReadOnly */
4423static DECLCALLBACK(bool) vdiIsReadOnly(PPDMIMEDIA pInterface)
4424{
4425 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
4426 LogFlow(("vdiIsReadOnly: returns %d\n", VDIDiskIsReadOnly(pData)));
4427 return VDIDiskIsReadOnly(pData);
4428}
4429
4430/** @copydoc PDMIMEDIA::pfnBiosGetTranslation */
4431static DECLCALLBACK(int) vdiBiosGetTranslation(PPDMIMEDIA pInterface, PPDMBIOSTRANSLATION penmTranslation)
4432{
4433 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
4434 int rc = VDIDiskGetTranslation(pData, penmTranslation);
4435 LogFlow(("vdiBiosGetTranslation: returns %Vrc (%d)\n", rc, *penmTranslation));
4436 return rc;
4437}
4438
4439/** @copydoc PDMIMEDIA::pfnBiosSetTranslation */
4440static DECLCALLBACK(int) vdiBiosSetTranslation(PPDMIMEDIA pInterface, PDMBIOSTRANSLATION enmTranslation)
4441{
4442 PVDIDISK pData = PDMIMEDIA_2_VDIDISK(pInterface);
4443 int rc = VDIDiskSetTranslation(pData, enmTranslation);
4444 LogFlow(("vdiBiosSetTranslation: returns %Vrc (%d)\n", rc, enmTranslation));
4445 return rc;
4446}
4447
4448
4449/**
4450 * Queries an interface to the driver.
4451 *
4452 * @returns Pointer to interface.
4453 * @returns NULL if the interface was not supported by the driver.
4454 * @param pInterface Pointer to this interface structure.
4455 * @param enmInterface The requested interface identification.
4456 * @thread Any thread.
4457 */
4458static DECLCALLBACK(void *) vdiQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
4459{
4460 PPDMDRVINS pDrvIns = PDMIBASE_2_DRVINS(pInterface);
4461 PVDIDISK pData = PDMINS2DATA(pDrvIns, PVDIDISK);
4462 switch (enmInterface)
4463 {
4464 case PDMINTERFACE_BASE:
4465 return &pDrvIns->IBase;
4466 case PDMINTERFACE_MEDIA:
4467 return &pData->IMedia;
4468 default:
4469 return NULL;
4470 }
4471}
4472
4473
4474/**
4475 * VBox HDD driver registration record.
4476 */
4477const PDMDRVREG g_DrvVBoxHDD =
4478{
4479 /* u32Version */
4480 PDM_DRVREG_VERSION,
4481 /* szDriverName */
4482 "VBoxHDD",
4483 /* pszDescription */
4484 "VBoxHDD media driver.",
4485 /* fFlags */
4486 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
4487 /* fClass. */
4488 PDM_DRVREG_CLASS_MEDIA,
4489 /* cMaxInstances */
4490 ~0,
4491 /* cbInstance */
4492 sizeof(VDIDISK),
4493 /* pfnConstruct */
4494 vdiConstruct,
4495 /* pfnDestruct */
4496 vdiDestruct,
4497 /* pfnIOCtl */
4498 NULL,
4499 /* pfnPowerOn */
4500 NULL,
4501 /* pfnReset */
4502 NULL,
4503 /* pfnSuspend */
4504 vdiSuspend,
4505 /* pfnResume */
4506 vdiResume,
4507 /* pfnDetach */
4508 NULL
4509};
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