VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VDICore.cpp@ 7636

最後變更 在這個檔案從7636是 7158,由 vboxsync 提交於 17 年 前

Various size_t related 64bit cleanness fixes in the VDI code.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 128.4 KB
 
1/** @file
2 * Virtual Disk Image (VDI), Core Code.
3 */
4
5/*
6 * Copyright (C) 2006-2007 innotek GmbH
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.alldomusa.eu.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16
17/*******************************************************************************
18* Header Files *
19*******************************************************************************/
20#define LOG_GROUP LOG_GROUP_DRV_VBOXHDD
21#include <VBox/VBoxHDD.h>
22#include "VDICore.h"
23#include <VBox/err.h>
24
25#include <VBox/log.h>
26#include <iprt/alloc.h>
27#include <iprt/assert.h>
28#include <iprt/uuid.h>
29#include <iprt/file.h>
30#include <iprt/string.h>
31#include <iprt/asm.h>
32
33
34/*******************************************************************************
35* Internal Functions *
36*******************************************************************************/
37static unsigned getPowerOfTwo(unsigned uNumber);
38static void vdiInitPreHeader(PVDIPREHEADER pPreHdr);
39static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr);
40static void vdiInitHeader(PVDIHEADER pHeader, VDIIMAGETYPE enmType, uint32_t fFlags,
41 const char *pszComment, uint64_t cbDisk, uint32_t cbBlock,
42 uint32_t cbBlockExtra);
43static int vdiValidateHeader(PVDIHEADER pHeader);
44static int vdiCreateImage(const char *pszFilename, VDIIMAGETYPE enmType, unsigned fFlags,
45 uint64_t cbSize, const char *pszComment, PVDIIMAGEDESC pParent,
46 PFNVMPROGRESS pfnProgress, void *pvUser);
47static void vdiInitImageDesc(PVDIIMAGEDESC pImage);
48static void vdiSetupImageDesc(PVDIIMAGEDESC pImage);
49static int vdiOpenImage(PVDIIMAGEDESC *ppImage, const char *pszFilename, unsigned fOpen,
50 PVDIIMAGEDESC pParent);
51static int vdiUpdateHeader(PVDIIMAGEDESC pImage);
52static int vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock);
53static int vdiUpdateBlocks(PVDIIMAGEDESC pImage);
54static void vdiSetModifiedFlag(PVDIIMAGEDESC pImage);
55static void vdiResetModifiedFlag(PVDIIMAGEDESC pImage);
56static void vdiDisableLastModifiedUpdate(PVDIIMAGEDESC pImage);
57#if 0 /* unused */
58static void vdiEnableLastModifiedUpdate(PVDIIMAGEDESC pImage);
59#endif
60static void vdiCloseImage(PVDIIMAGEDESC pImage);
61static int vdiReadInBlock(PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offRead,
62 size_t cbToRead, void *pvBuf);
63static int vdiFillBlockByZeroes(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock);
64static int vdiWriteInBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock,
65 unsigned offWrite, size_t cbToWrite, const void *pvBuf);
66static int vdiCopyBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock);
67static int vdiMergeImages(PVDIIMAGEDESC pImageFrom, PVDIIMAGEDESC pImageTo, bool fParentToChild,
68 PFNVMPROGRESS pfnProgress, void *pvUser);
69static void vdiAddImageToList(PVDIDISK pDisk, PVDIIMAGEDESC pImage);
70static void vdiRemoveImageFromList(PVDIDISK pDisk, PVDIIMAGEDESC pImage);
71static PVDIIMAGEDESC vdiGetImageByNumber(PVDIDISK pDisk, int nImage);
72static int vdiUpdateReadOnlyHeader(PVDIIMAGEDESC pImage);
73
74static int vdiCommitToImage(PVDIDISK pDisk, PVDIIMAGEDESC pDstImage,
75 PFNVMPROGRESS pfnProgress, void *pvUser);
76static void vdiDumpImage(PVDIIMAGEDESC pImage);
77
78
79/**
80 * internal: return power of 2 or 0 if num error.
81 */
82static unsigned getPowerOfTwo(unsigned uNumber)
83{
84 if (uNumber == 0)
85 return 0;
86 unsigned uPower2 = 0;
87 while ((uNumber & 1) == 0)
88 {
89 uNumber >>= 1;
90 uPower2++;
91 }
92 return uNumber == 1 ? uPower2 : 0;
93}
94
95/**
96 * internal: init HDD preheader.
97 */
98static void vdiInitPreHeader(PVDIPREHEADER pPreHdr)
99{
100 pPreHdr->u32Signature = VDI_IMAGE_SIGNATURE;
101 pPreHdr->u32Version = VDI_IMAGE_VERSION;
102 memset(pPreHdr->szFileInfo, 0, sizeof(pPreHdr->szFileInfo));
103 strncat(pPreHdr->szFileInfo, VDI_IMAGE_FILE_INFO, sizeof(pPreHdr->szFileInfo));
104}
105
106/**
107 * internal: check HDD preheader.
108 */
109static int vdiValidatePreHeader(PVDIPREHEADER pPreHdr)
110{
111 if (pPreHdr->u32Signature != VDI_IMAGE_SIGNATURE)
112 return VERR_VDI_INVALID_SIGNATURE;
113
114 if ( VDI_GET_VERSION_MAJOR(pPreHdr->u32Version) != VDI_IMAGE_VERSION_MAJOR
115 && pPreHdr->u32Version != 0x00000002) /* old version. */
116 return VERR_VDI_UNSUPPORTED_VERSION;
117
118 return VINF_SUCCESS;
119}
120
121/**
122 * internal: init HDD header. Always use latest header version.
123 * @param pHeader Assumes it was initially initialized to all zeros.
124 */
125static void vdiInitHeader(PVDIHEADER pHeader, VDIIMAGETYPE enmType, uint32_t fFlags,
126 const char *pszComment, uint64_t cbDisk, uint32_t cbBlock,
127 uint32_t cbBlockExtra)
128{
129 pHeader->uVersion = VDI_IMAGE_VERSION;
130 pHeader->u.v1.cbHeader = sizeof(VDIHEADER1);
131 pHeader->u.v1.u32Type = (uint32_t)enmType;
132 pHeader->u.v1.fFlags = fFlags;
133#ifdef VBOX_STRICT
134 char achZero[VDI_IMAGE_COMMENT_SIZE] = {0};
135 Assert(!memcmp(pHeader->u.v1.szComment, achZero, VDI_IMAGE_COMMENT_SIZE));
136#endif
137 pHeader->u.v1.szComment[0] = '\0';
138 if (pszComment)
139 {
140 AssertMsg(strlen(pszComment) < sizeof(pHeader->u.v1.szComment),
141 ("HDD Comment is too long, cb=%d\n", strlen(pszComment)));
142 strncat(pHeader->u.v1.szComment, pszComment, sizeof(pHeader->u.v1.szComment));
143 }
144
145 /* Mark the legacy geometry not-calculated. */
146 pHeader->u.v1.LegacyGeometry.cCylinders = 0;
147 pHeader->u.v1.LegacyGeometry.cHeads = 0;
148 pHeader->u.v1.LegacyGeometry.cSectors = 0;
149 pHeader->u.v1.LegacyGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
150 pHeader->u.v1.u32Dummy = 0; /* used to be the translation value */
151
152 pHeader->u.v1.cbDisk = cbDisk;
153 pHeader->u.v1.cbBlock = cbBlock;
154 pHeader->u.v1.cBlocks = (uint32_t)(cbDisk / cbBlock);
155 if (cbDisk % cbBlock)
156 pHeader->u.v1.cBlocks++;
157 pHeader->u.v1.cbBlockExtra = cbBlockExtra;
158 pHeader->u.v1.cBlocksAllocated = 0;
159
160 /* Init offsets. */
161 pHeader->u.v1.offBlocks = RT_ALIGN_32(sizeof(VDIPREHEADER) + sizeof(VDIHEADER1), VDI_GEOMETRY_SECTOR_SIZE);
162 pHeader->u.v1.offData = RT_ALIGN_32(pHeader->u.v1.offBlocks + (pHeader->u.v1.cBlocks * sizeof(VDIIMAGEBLOCKPOINTER)), VDI_GEOMETRY_SECTOR_SIZE);
163
164 /* Init uuids. */
165 RTUuidCreate(&pHeader->u.v1.uuidCreate);
166 RTUuidClear(&pHeader->u.v1.uuidModify);
167 RTUuidClear(&pHeader->u.v1.uuidLinkage);
168 RTUuidClear(&pHeader->u.v1.uuidParentModify);
169
170 /* Mark LCHS geometry not-calculated. */
171 pHeader->u.v1plus.LCHSGeometry.cCylinders = 0;
172 pHeader->u.v1plus.LCHSGeometry.cHeads = 0;
173 pHeader->u.v1plus.LCHSGeometry.cSectors = 0;
174 pHeader->u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
175}
176
177/**
178 * internal: check HDD header.
179 */
180static int vdiValidateHeader(PVDIHEADER pHeader)
181{
182 /* Check verion-dependend header parameters. */
183 switch (GET_MAJOR_HEADER_VERSION(pHeader))
184 {
185 case 0:
186 {
187 /* Old header version. */
188 break;
189 }
190 case 1:
191 {
192 /* Current header version. */
193
194 if (pHeader->u.v1.cbHeader < sizeof(VDIHEADER1))
195 {
196 LogRel(("VDI: v1 header size wrong (%d < %d)\n",
197 pHeader->u.v1.cbHeader, sizeof(VDIHEADER1)));
198 return VERR_VDI_INVALID_HEADER;
199 }
200
201 if (getImageBlocksOffset(pHeader) < (sizeof(VDIPREHEADER) + sizeof(VDIHEADER1)))
202 {
203 LogRel(("VDI: v1 blocks offset wrong (%d < %d)\n",
204 getImageBlocksOffset(pHeader), sizeof(VDIPREHEADER) + sizeof(VDIHEADER1)));
205 return VERR_VDI_INVALID_HEADER;
206 }
207
208 if (getImageDataOffset(pHeader) < (getImageBlocksOffset(pHeader) + getImageBlocks(pHeader) * sizeof(VDIIMAGEBLOCKPOINTER)))
209 {
210 LogRel(("VDI: v1 image data offset wrong (%d < %d)\n",
211 getImageDataOffset(pHeader), getImageBlocksOffset(pHeader) + getImageBlocks(pHeader) * sizeof(VDIIMAGEBLOCKPOINTER)));
212 return VERR_VDI_INVALID_HEADER;
213 }
214
215 if ( getImageType(pHeader) == VDI_IMAGE_TYPE_UNDO
216 || getImageType(pHeader) == VDI_IMAGE_TYPE_DIFF)
217 {
218 if (RTUuidIsNull(getImageParentUUID(pHeader)))
219 {
220 LogRel(("VDI: v1 uuid of parent is 0)\n"));
221 return VERR_VDI_INVALID_HEADER;
222 }
223 if (RTUuidIsNull(getImageParentModificationUUID(pHeader)))
224 {
225 LogRel(("VDI: v1 uuid of parent modification is 0\n"));
226 return VERR_VDI_INVALID_HEADER;
227 }
228 }
229
230 break;
231 }
232 default:
233 /* Unsupported. */
234 return VERR_VDI_UNSUPPORTED_VERSION;
235 }
236
237 /* Check common header parameters. */
238
239 bool fFailed = false;
240
241 if ( getImageType(pHeader) < VDI_IMAGE_TYPE_FIRST
242 || getImageType(pHeader) > VDI_IMAGE_TYPE_LAST)
243 {
244 LogRel(("VDI: bad image type %d\n", getImageType(pHeader)));
245 fFailed = true;
246 }
247
248 if (getImageFlags(pHeader) & ~VDI_IMAGE_FLAGS_MASK)
249 {
250 LogRel(("VDI: bad image flags %08x\n", getImageFlags(pHeader)));
251 fFailed = true;
252 }
253
254 if ( getImageLCHSGeometry(pHeader)
255 && (getImageLCHSGeometry(pHeader))->cbSector != VDI_GEOMETRY_SECTOR_SIZE)
256 {
257 LogRel(("VDI: wrong sector size (%d != %d)\n",
258 (getImageLCHSGeometry(pHeader))->cbSector, VDI_GEOMETRY_SECTOR_SIZE));
259 fFailed = true;
260 }
261
262 if ( getImageDiskSize(pHeader) == 0
263 || getImageBlockSize(pHeader) == 0
264 || getImageBlocks(pHeader) == 0
265 || getPowerOfTwo(getImageBlockSize(pHeader)) == 0)
266 {
267 LogRel(("VDI: wrong size (%lld, %d, %d, %d)\n",
268 getImageDiskSize(pHeader), getImageBlockSize(pHeader),
269 getImageBlocks(pHeader), getPowerOfTwo(getImageBlockSize(pHeader))));
270 fFailed = true;
271 }
272
273 if (getImageBlocksAllocated(pHeader) > getImageBlocks(pHeader))
274 {
275 LogRel(("VDI: too many blocks allocated (%d > %d)\n"
276 " blocksize=%d disksize=%lld\n",
277 getImageBlocksAllocated(pHeader), getImageBlocks(pHeader),
278 getImageBlockSize(pHeader), getImageDiskSize(pHeader)));
279 fFailed = true;
280 }
281
282 if ( getImageExtraBlockSize(pHeader) != 0
283 && getPowerOfTwo(getImageExtraBlockSize(pHeader)) == 0)
284 {
285 LogRel(("VDI: wrong extra size (%d, %d)\n",
286 getImageExtraBlockSize(pHeader), getPowerOfTwo(getImageExtraBlockSize(pHeader))));
287 fFailed = true;
288 }
289
290 if ((uint64_t)getImageBlockSize(pHeader) * getImageBlocks(pHeader) < getImageDiskSize(pHeader))
291 {
292 LogRel(("VDI: wrong disk size (%d, %d, %lld)\n",
293 getImageBlockSize(pHeader), getImageBlocks(pHeader), getImageDiskSize(pHeader)));
294 fFailed = true;
295 }
296
297 if (RTUuidIsNull(getImageCreationUUID(pHeader)))
298 {
299 LogRel(("VDI: uuid of creator is 0\n"));
300 fFailed = true;
301 }
302
303 if (RTUuidIsNull(getImageModificationUUID(pHeader)))
304 {
305 LogRel(("VDI: uuid of modificator is 0\n"));
306 fFailed = true;
307 }
308
309 return fFailed ? VERR_VDI_INVALID_HEADER : VINF_SUCCESS;
310}
311
312/**
313 * internal: init VDIIMAGEDESC structure.
314 */
315static void vdiInitImageDesc(PVDIIMAGEDESC pImage)
316{
317 pImage->pPrev = NULL;
318 pImage->pNext = NULL;
319 pImage->File = NIL_RTFILE;
320 pImage->paBlocks = NULL;
321}
322
323/**
324 * internal: setup VDIIMAGEDESC structure by image header.
325 */
326static void vdiSetupImageDesc(PVDIIMAGEDESC pImage)
327{
328 pImage->fFlags = getImageFlags(&pImage->Header);
329 pImage->offStartBlocks = getImageBlocksOffset(&pImage->Header);
330 pImage->offStartData = getImageDataOffset(&pImage->Header);
331 pImage->uBlockMask = getImageBlockSize(&pImage->Header) - 1;
332 pImage->uShiftIndex2Offset =
333 pImage->uShiftOffset2Index = getPowerOfTwo(getImageBlockSize(&pImage->Header));
334 pImage->offStartBlockData = getImageExtraBlockSize(&pImage->Header);
335 if (pImage->offStartBlockData != 0)
336 pImage->uShiftIndex2Offset += getPowerOfTwo(pImage->offStartBlockData);
337}
338
339/**
340 * internal: create image.
341 */
342static int vdiCreateImage(const char *pszFilename, VDIIMAGETYPE enmType, unsigned fFlags,
343 uint64_t cbSize, const char *pszComment, PVDIIMAGEDESC pParent,
344 PFNVMPROGRESS pfnProgress, void *pvUser)
345{
346 /* Check args. */
347 Assert(pszFilename);
348 Assert(enmType >= VDI_IMAGE_TYPE_FIRST && enmType <= VDI_IMAGE_TYPE_LAST);
349 Assert(!(fFlags & ~VDI_IMAGE_FLAGS_MASK));
350 Assert(cbSize);
351
352 /* Special check for comment length. */
353 if ( pszComment
354 && strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
355 {
356 Log(("vdiCreateImage: pszComment is too long, cb=%d\n", strlen(pszComment)));
357 return VERR_VDI_COMMENT_TOO_LONG;
358 }
359
360 if ( enmType == VDI_IMAGE_TYPE_UNDO
361 || enmType == VDI_IMAGE_TYPE_DIFF)
362 {
363 Assert(pParent);
364 if (VDI_GET_VERSION_MAJOR(pParent->PreHeader.u32Version) != VDI_IMAGE_VERSION_MAJOR)
365 {
366 /* Invalid parent image version. */
367 Log(("vdiCreateImage: unsupported parent version=%08X\n", pParent->PreHeader.u32Version));
368 return VERR_VDI_UNSUPPORTED_VERSION;
369 }
370
371 /* get image params from the parent image. */
372 fFlags = getImageFlags(&pParent->Header);
373 cbSize = getImageDiskSize(&pParent->Header);
374 }
375
376 PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
377 if (!pImage)
378 return VERR_NO_MEMORY;
379 vdiInitImageDesc(pImage);
380
381 vdiInitPreHeader(&pImage->PreHeader);
382 vdiInitHeader(&pImage->Header, enmType, fFlags, pszComment, cbSize, VDI_IMAGE_DEFAULT_BLOCK_SIZE, 0);
383
384 if ( enmType == VDI_IMAGE_TYPE_UNDO
385 || enmType == VDI_IMAGE_TYPE_DIFF)
386 {
387 /* Set up linkage information. */
388 pImage->Header.u.v1.uuidLinkage = *getImageCreationUUID(&pParent->Header);
389 pImage->Header.u.v1.uuidParentModify = *getImageModificationUUID(&pParent->Header);
390 }
391
392 pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
393 if (!pImage->paBlocks)
394 {
395 RTMemFree(pImage);
396 return VERR_NO_MEMORY;
397 }
398
399 if (enmType != VDI_IMAGE_TYPE_FIXED)
400 {
401 /* for growing images mark all blocks in paBlocks as free. */
402 for (unsigned i = 0; i < pImage->Header.u.v1.cBlocks; i++)
403 pImage->paBlocks[i] = VDI_IMAGE_BLOCK_FREE;
404 }
405 else
406 {
407 /* for fixed images mark all blocks in paBlocks as allocated */
408 for (unsigned i = 0; i < pImage->Header.u.v1.cBlocks; i++)
409 pImage->paBlocks[i] = i;
410 pImage->Header.u.v1.cBlocksAllocated = pImage->Header.u.v1.cBlocks;
411 }
412
413 /* Setup image parameters. */
414 vdiSetupImageDesc(pImage);
415
416 /* create file */
417 int rc = RTFileOpen(&pImage->File,
418 pszFilename,
419 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL | RTFILE_O_NOT_CONTENT_INDEXED);
420 if (VBOX_SUCCESS(rc))
421 {
422 /* Lock image exclusively to close any wrong access by VDI API calls. */
423 uint64_t cbLock = pImage->offStartData
424 + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
425
426 if (enmType == VDI_IMAGE_TYPE_FIXED)
427 {
428 /* check the free space on the disk and leave early if there is not
429 * sufficient space available */
430 RTFOFF cbFree = 0;
431 rc = RTFsQuerySizes(pszFilename, NULL, &cbFree, NULL, NULL);
432 if (VBOX_SUCCESS(rc) /* ignore errors */ && ((uint64_t)cbFree < cbLock))
433 {
434 rc = VERR_DISK_FULL;
435 cbLock = 0;
436 goto l_create_failed;
437 }
438 }
439
440 rc = RTFileLock(pImage->File,
441 RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
442 if (VBOX_FAILURE(rc))
443 {
444 cbLock = 0; /* Not locked. */
445 goto l_create_failed;
446 }
447
448 if (enmType == VDI_IMAGE_TYPE_FIXED)
449 {
450 /*
451 * Allocate & commit whole file if fixed image, it must be more
452 * effective than expanding file by write operations.
453 */
454 rc = RTFileSetSize(pImage->File, cbLock);
455 }
456 else
457 {
458 /* Set file size to hold header and blocks array. */
459 rc = RTFileSetSize(pImage->File, pImage->offStartData);
460 }
461 if (VBOX_FAILURE(rc))
462 goto l_create_failed;
463
464 /* Generate image last-modify uuid */
465 RTUuidCreate(getImageModificationUUID(&pImage->Header));
466
467 /* Write pre-header. */
468 rc = RTFileWrite(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
469 if (VBOX_FAILURE(rc))
470 goto l_create_failed;
471
472 /* Write header. */
473 rc = RTFileWrite(pImage->File, &pImage->Header.u.v1plus, sizeof(pImage->Header.u.v1plus), NULL);
474 if (VBOX_FAILURE(rc))
475 goto l_create_failed;
476
477 /* Write blocks array. */
478 rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
479 if (VBOX_FAILURE(rc))
480 goto l_create_failed;
481 rc = RTFileWrite(pImage->File,
482 pImage->paBlocks,
483 getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER),
484 NULL);
485 if (VBOX_FAILURE(rc))
486 goto l_create_failed;
487
488 if (enmType == VDI_IMAGE_TYPE_FIXED)
489 {
490 /* Fill image with zeroes. We do this for every fixed-size image since on some systems
491 * (for example Windows Vista), it takes ages to write a block near the end of a sparse
492 * file and the guest could complain about an ATA timeout. */
493
494 /** @todo Starting with Linux 2.6.23, there is an fallocate() system call.
495 * Currently supported file systems are ext4 and ocfs2. */
496
497 rc = RTFileSeek(pImage->File, pImage->offStartData, RTFILE_SEEK_BEGIN, NULL);
498 if (VBOX_FAILURE(rc))
499 goto l_create_failed;
500
501 /* Allocate a temporary zero-filled buffer. Use a bigger block size to optimize writing */
502 const size_t cbBuf = 128 * _1K;
503 void *pvBuf = RTMemTmpAllocZ(cbBuf);
504 if (pvBuf)
505 {
506 uint64_t cbFill = (uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset;
507 uint64_t cbDisk = cbFill;
508
509 /* do loop to fill all image. */
510 while (cbFill > 0)
511 {
512 unsigned to_fill = (unsigned)RT_MIN(cbFill, cbBuf);
513
514 rc = RTFileWrite(pImage->File, pvBuf, to_fill, NULL);
515 if (VBOX_FAILURE(rc))
516 break;
517
518 cbFill -= to_fill;
519
520 if (pfnProgress)
521 {
522 rc = pfnProgress(NULL /* WARNING! pVM=NULL */,
523 (unsigned)(((cbDisk - cbFill) * 100) / cbDisk),
524 pvUser);
525 if (VBOX_FAILURE(rc))
526 break;
527 }
528 }
529 RTMemTmpFree(pvBuf);
530 }
531 else
532 {
533 /* alloc error */
534 rc = VERR_NO_MEMORY;
535 }
536 }
537
538 l_create_failed:
539
540 if (cbLock)
541 RTFileUnlock(pImage->File, 0, cbLock);
542
543 RTFileClose(pImage->File);
544
545 /* Delete image file if error occured while creating */
546 if (VBOX_FAILURE(rc))
547 RTFileDelete(pszFilename);
548 }
549
550 RTMemFree(pImage->paBlocks);
551 RTMemFree(pImage);
552
553 if ( VBOX_SUCCESS(rc)
554 && pfnProgress)
555 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
556
557 Log(("vdiCreateImage: done, filename=\"%s\", rc=%Vrc\n", pszFilename, rc));
558
559 return rc;
560}
561
562/**
563 * Open an image.
564 * @internal
565 */
566static int vdiOpenImage(PVDIIMAGEDESC *ppImage, const char *pszFilename,
567 unsigned fOpen, PVDIIMAGEDESC pParent)
568{
569 /*
570 * Validate input.
571 */
572 Assert(ppImage);
573 Assert(pszFilename);
574 Assert(!(fOpen & ~VDI_OPEN_FLAGS_MASK));
575
576 PVDIIMAGEDESC pImage;
577 size_t cchFilename = strlen(pszFilename);
578 if (cchFilename >= sizeof(pImage->szFilename))
579 {
580 AssertMsgFailed(("filename=\"%s\" is too long (%d bytes)!\n", pszFilename, cchFilename));
581 return VERR_FILENAME_TOO_LONG;
582 }
583
584 pImage = (PVDIIMAGEDESC)RTMemAllocZ(sizeof(VDIIMAGEDESC));
585 if (!pImage)
586 return VERR_NO_MEMORY;
587 vdiInitImageDesc(pImage);
588
589 memcpy(pImage->szFilename, pszFilename, cchFilename);
590 pImage->fOpen = fOpen;
591
592 /*
593 * Open the image.
594 */
595 int rc = RTFileOpen(&pImage->File,
596 pImage->szFilename,
597 fOpen & VDI_OPEN_FLAGS_READONLY
598 ? RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE
599 : RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
600 if (VBOX_FAILURE(rc))
601 {
602 if (!(fOpen & VDI_OPEN_FLAGS_READONLY))
603 {
604 /* Try to open image for reading only. */
605 rc = RTFileOpen(&pImage->File,
606 pImage->szFilename,
607 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
608 if (VBOX_SUCCESS(rc))
609 pImage->fOpen |= VDI_OPEN_FLAGS_READONLY;
610 }
611 if (VBOX_FAILURE(rc))
612 {
613 RTMemFree(pImage);
614 return rc;
615 }
616 }
617 /* Set up current image r/w state. */
618 pImage->fReadOnly = !!(pImage->fOpen & VDI_OPEN_FLAGS_READONLY);
619
620 /*
621 * Set initial file lock for reading header only.
622 * Length of lock doesn't matter, it just must include image header.
623 */
624 uint64_t cbLock = _1M;
625 rc = RTFileLock(pImage->File, RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
626 if (VBOX_FAILURE(rc))
627 {
628 cbLock = 0;
629 goto l_open_failed;
630 }
631
632 /* Read pre-header. */
633 rc = RTFileRead(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
634 if (VBOX_FAILURE(rc))
635 goto l_open_failed;
636 rc = vdiValidatePreHeader(&pImage->PreHeader);
637 if (VBOX_FAILURE(rc))
638 goto l_open_failed;
639
640 /* Read header. */
641 pImage->Header.uVersion = pImage->PreHeader.u32Version;
642 switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
643 {
644 case 0:
645 rc = RTFileRead(pImage->File, &pImage->Header.u.v0, sizeof(pImage->Header.u.v0), NULL);
646 break;
647 case 1:
648 switch (GET_MINOR_HEADER_VERSION(&pImage->Header))
649 {
650 case 1:
651 rc = RTFileRead(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
652 /* Convert VDI 1.1 images to VDI 1.1+ on open in read/write
653 * mode. Conversion is harmless, as any VirtualBox version
654 * supporting VDI 1.1 doesn't touch fields it doesn't know
655 * about. And it accepts bigger headers. */
656 if ( VBOX_SUCCESS(rc)
657 && !pImage->fReadOnly
658 && pImage->Header.u.v1.cbHeader < sizeof(pImage->Header.u.v1plus))
659 {
660 pImage->Header.u.v1plus.cbHeader = sizeof(pImage->Header.u.v1plus);
661 /* Mark LCHS geometry not-calculated. */
662 pImage->Header.u.v1plus.LCHSGeometry.cCylinders = 0;
663 pImage->Header.u.v1plus.LCHSGeometry.cHeads = 0;
664 pImage->Header.u.v1plus.LCHSGeometry.cSectors = 0;
665 pImage->Header.u.v1plus.LCHSGeometry.cbSector = VDI_GEOMETRY_SECTOR_SIZE;
666 }
667 else if ( VBOX_SUCCESS(rc)
668 && pImage->Header.u.v1.cbHeader >= sizeof(pImage->Header.u.v1plus))
669 {
670 /* Read the actual VDI 1.1+ header completely. */
671 rc = RTFileReadAt(pImage->File, sizeof(pImage->PreHeader), &pImage->Header.u.v1plus, sizeof(pImage->Header.u.v1plus), NULL);
672 }
673 break;
674 default:
675 rc = VERR_VDI_UNSUPPORTED_VERSION;
676 break;
677 }
678 break;
679 default:
680 rc = VERR_VDI_UNSUPPORTED_VERSION;
681 break;
682 }
683 if (VBOX_FAILURE(rc))
684 goto l_open_failed;
685
686 rc = vdiValidateHeader(&pImage->Header);
687 if (VBOX_FAILURE(rc))
688 goto l_open_failed;
689
690 /* Check diff image correctness. */
691 if (pParent)
692 {
693 if (pImage->PreHeader.u32Version != pParent->PreHeader.u32Version)
694 {
695 rc = VERR_VDI_IMAGES_VERSION_MISMATCH;
696 goto l_open_failed;
697 }
698
699 if ( getImageType(&pImage->Header) != VDI_IMAGE_TYPE_UNDO
700 && getImageType(&pImage->Header) != VDI_IMAGE_TYPE_DIFF)
701 {
702 rc = VERR_VDI_WRONG_DIFF_IMAGE;
703 goto l_open_failed;
704 }
705
706 if ( getImageDiskSize(&pImage->Header) != getImageDiskSize(&pParent->Header)
707 || getImageBlockSize(&pImage->Header) != getImageBlockSize(&pParent->Header)
708 || getImageBlocks(&pImage->Header) != getImageBlocks(&pParent->Header)
709 || getImageExtraBlockSize(&pImage->Header) != getImageExtraBlockSize(&pParent->Header))
710 {
711 rc = VERR_VDI_WRONG_DIFF_IMAGE;
712 goto l_open_failed;
713 }
714
715 /* Check linkage data. */
716 if ( RTUuidCompare(getImageParentUUID(&pImage->Header),
717 getImageCreationUUID(&pParent->Header))
718 || RTUuidCompare(getImageParentModificationUUID(&pImage->Header),
719 getImageModificationUUID(&pParent->Header)))
720 {
721 rc = VERR_VDI_IMAGES_UUID_MISMATCH;
722 goto l_open_failed;
723 }
724 }
725
726 /* Setup image parameters by header. */
727 vdiSetupImageDesc(pImage);
728
729 /* reset modified flag into first-modified state. */
730 pImage->fModified = VDI_IMAGE_MODIFIED_FIRST;
731
732 /* Image is validated, set working file lock on it. */
733 rc = RTFileUnlock(pImage->File, 0, cbLock);
734 AssertRC(rc);
735 cbLock = pImage->offStartData
736 + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
737 rc = RTFileLock(pImage->File,
738 (pImage->fReadOnly) ?
739 RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY :
740 RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY,
741 0,
742 cbLock);
743 if ( VBOX_FAILURE(rc)
744 && !pImage->fReadOnly)
745 {
746 /* Failed to lock image for writing, try read-only lock. */
747 rc = RTFileLock(pImage->File,
748 RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY, 0, cbLock);
749 if (VBOX_SUCCESS(rc))
750 pImage->fReadOnly = true;
751 }
752 if (VBOX_FAILURE(rc))
753 {
754 cbLock = 0; /* Not locked. */
755 goto l_open_failed;
756 }
757
758 /* Allocate memory for blocks array. */
759 pImage->paBlocks = (PVDIIMAGEBLOCKPOINTER)RTMemAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header));
760 if (!pImage->paBlocks)
761 {
762 rc = VERR_NO_MEMORY;
763 goto l_open_failed;
764 }
765
766 /* Read blocks array. */
767 rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
768 if (VBOX_FAILURE(rc))
769 goto l_open_failed;
770 rc = RTFileRead(pImage->File, pImage->paBlocks,
771 getImageBlocks(&pImage->Header) * sizeof(VDIIMAGEBLOCKPOINTER), NULL);
772 if (VBOX_FAILURE(rc))
773 goto l_open_failed;
774
775 /* all done. */
776 *ppImage = pImage;
777 return VINF_SUCCESS;
778
779l_open_failed:
780 /* Clean up. */
781 if (pImage->paBlocks)
782 RTMemFree(pImage->paBlocks);
783 if (cbLock)
784 RTFileUnlock(pImage->File, 0, cbLock);
785 RTFileClose(pImage->File);
786 RTMemFree(pImage);
787 Log(("vdiOpenImage: failed, filename=\"%s\", rc=%Vrc\n", pszFilename, rc));
788 return rc;
789}
790
791/**
792 * internal: save header to file.
793 */
794static int vdiUpdateHeader(PVDIIMAGEDESC pImage)
795{
796 /* Seek to header start. */
797 int rc = RTFileSeek(pImage->File, sizeof(VDIPREHEADER), RTFILE_SEEK_BEGIN, NULL);
798 if (VBOX_SUCCESS(rc))
799 {
800 switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
801 {
802 case 0:
803 rc = RTFileWrite(pImage->File, &pImage->Header.u.v0, sizeof(pImage->Header.u.v0), NULL);
804 break;
805 case 1:
806 switch (GET_MAJOR_HEADER_VERSION(&pImage->Header))
807 {
808 case 1:
809 if (pImage->Header.u.v1plus.cbHeader < sizeof(pImage->Header.u.v1plus))
810 rc = RTFileWrite(pImage->File, &pImage->Header.u.v1, sizeof(pImage->Header.u.v1), NULL);
811 else
812 rc = RTFileWrite(pImage->File, &pImage->Header.u.v1plus, sizeof(pImage->Header.u.v1plus), NULL);
813 break;
814 default:
815 rc = VERR_VDI_UNSUPPORTED_VERSION;
816 break;
817 }
818 break;
819 default:
820 rc = VERR_VDI_UNSUPPORTED_VERSION;
821 break;
822 }
823 }
824 AssertMsgRC(rc, ("vdiUpdateHeader failed, filename=\"%s\" rc=%Vrc\n", pImage->szFilename, rc));
825 return rc;
826}
827
828/**
829 * internal: save block pointer to file, save header to file.
830 */
831static int vdiUpdateBlockInfo(PVDIIMAGEDESC pImage, unsigned uBlock)
832{
833 /* Update image header. */
834 int rc = vdiUpdateHeader(pImage);
835 if (VBOX_SUCCESS(rc))
836 {
837 /* write only one block pointer. */
838 rc = RTFileSeek(pImage->File,
839 pImage->offStartBlocks + uBlock * sizeof(VDIIMAGEBLOCKPOINTER),
840 RTFILE_SEEK_BEGIN,
841 NULL);
842 if (VBOX_SUCCESS(rc))
843 rc = RTFileWrite(pImage->File,
844 &pImage->paBlocks[uBlock],
845 sizeof(VDIIMAGEBLOCKPOINTER),
846 NULL);
847 AssertMsgRC(rc, ("vdiUpdateBlockInfo failed to update block=%u, filename=\"%s\", rc=%Vrc\n",
848 uBlock, pImage->szFilename, rc));
849 }
850 return rc;
851}
852
853/**
854 * internal: save blocks array to file, save header to file.
855 */
856static int vdiUpdateBlocks(PVDIIMAGEDESC pImage)
857{
858 /* Update image header. */
859 int rc = vdiUpdateHeader(pImage);
860 if (VBOX_SUCCESS(rc))
861 {
862 /* write the block pointers array. */
863 rc = RTFileSeek(pImage->File, pImage->offStartBlocks, RTFILE_SEEK_BEGIN, NULL);
864 if (VBOX_SUCCESS(rc))
865 rc = RTFileWrite(pImage->File,
866 pImage->paBlocks,
867 sizeof(VDIIMAGEBLOCKPOINTER) * getImageBlocks(&pImage->Header),
868 NULL);
869 AssertMsgRC(rc, ("vdiUpdateBlocks failed, filename=\"%s\", rc=%Vrc\n",
870 pImage->szFilename, rc));
871 }
872 return rc;
873}
874
875/**
876 * internal: mark image as modified, if this is the first change - update image header
877 * on disk with a new uuidModify value.
878 */
879static void vdiSetModifiedFlag(PVDIIMAGEDESC pImage)
880{
881 pImage->fModified |= VDI_IMAGE_MODIFIED_FLAG;
882 if (pImage->fModified & VDI_IMAGE_MODIFIED_FIRST)
883 {
884 pImage->fModified &= ~VDI_IMAGE_MODIFIED_FIRST;
885
886 /* first modify - generate uuidModify and save to file. */
887 vdiResetModifiedFlag(pImage);
888
889 if (!(pImage->fModified | VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
890 {
891 /* save header to file,
892 * note: no rc checking.
893 */
894 vdiUpdateHeader(pImage);
895 }
896 }
897}
898
899/**
900 * internal: generate new uuidModify if the image was changed.
901 */
902static void vdiResetModifiedFlag(PVDIIMAGEDESC pImage)
903{
904 if (pImage->fModified & VDI_IMAGE_MODIFIED_FLAG)
905 {
906 /* generate new last-modified uuid */
907 if (!(pImage->fModified | VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
908 RTUuidCreate(getImageModificationUUID(&pImage->Header));
909
910 pImage->fModified &= ~VDI_IMAGE_MODIFIED_FLAG;
911 }
912}
913
914/**
915 * internal: disables updates of the last-modified UUID
916 * when performing image writes.
917 */
918static void vdiDisableLastModifiedUpdate(PVDIIMAGEDESC pImage)
919{
920 pImage->fModified |= VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE;
921}
922
923#if 0 /* unused */
924/**
925 * internal: enables updates of the last-modified UUID
926 * when performing image writes.
927 */
928static void vdiEnableLastModifiedUpdate(PVDIIMAGEDESC pImage)
929{
930 pImage->fModified &= ~VDI_IMAGE_MODIFIED_DISABLE_UUID_UPDATE;
931}
932#endif
933
934/**
935 * Flush the image file to disk.
936 */
937void VDIFlushImage(PVDIIMAGEDESC pImage)
938{
939 if (!pImage->fReadOnly)
940 {
941 /* Update last-modified uuid if need. */
942 vdiResetModifiedFlag(pImage);
943
944 /* Save header. */
945 int rc = vdiUpdateHeader(pImage);
946 AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
947 pImage->szFilename, rc));
948 RTFileFlush(pImage->File);
949 }
950}
951
952/**
953 * internal: close image file.
954 */
955static void vdiCloseImage(PVDIIMAGEDESC pImage)
956{
957 /* Params checking. */
958 Assert(pImage);
959 Assert(pImage->File != NIL_RTFILE);
960
961 VDIFlushImage(pImage);
962 RTFileUnlock(pImage->File,
963 0,
964 pImage->offStartData
965 + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset));
966 RTFileClose(pImage->File);
967
968 /* free image resources */
969 RTMemFree(pImage->paBlocks);
970 RTMemFree(pImage);
971}
972
973/**
974 * internal: read data inside image block.
975 *
976 * note: uBlock must be valid, readed data must not overlap block bounds.
977 */
978static int vdiReadInBlock(PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offRead,
979 size_t cbToRead, void *pvBuf)
980{
981 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
982 {
983 /* block present in image file */
984 uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
985 + (pImage->offStartData + pImage->offStartBlockData + offRead);
986 int rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
987 if (VBOX_SUCCESS(rc))
988 rc = RTFileRead(pImage->File, pvBuf, cbToRead, NULL);
989 if (VBOX_FAILURE(rc))
990 Log(("vdiReadInBlock: rc=%Vrc filename=\"%s\" uBlock=%u offRead=%u cbToRead=%u u64Offset=%llu\n",
991 rc, pImage->szFilename, uBlock, offRead, cbToRead, u64Offset));
992 return rc;
993 }
994
995 /* Returns zeroes for both free and zero block types. */
996 memset(pvBuf, 0, cbToRead);
997 return VINF_SUCCESS;
998}
999
1000/**
1001 * Read data from virtual HDD.
1002 *
1003 * @returns VBox status code.
1004 * @param pDisk Pointer to VDI HDD container.
1005 * @param offStart Offset of first reading byte from start of disk.
1006 * @param pvBuf Pointer to buffer for reading data.
1007 * @param cbToRead Number of bytes to read.
1008 */
1009VBOXDDU_DECL(int) VDIDiskRead(PVDIDISK pDisk, uint64_t offStart, void *pvBuf, size_t cbToRead)
1010{
1011 /* sanity check */
1012 Assert(pDisk);
1013 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1014
1015 PVDIIMAGEDESC pImage = pDisk->pLast;
1016 Assert(pImage);
1017
1018 /* Check params. */
1019 if ( offStart + cbToRead > getImageDiskSize(&pImage->Header)
1020 || cbToRead == 0)
1021 {
1022 AssertMsgFailed(("offStart=%llu cbToRead=%u\n", offStart, cbToRead));
1023 return VERR_INVALID_PARAMETER;
1024 }
1025
1026 /* Calculate starting block number and offset inside it. */
1027 unsigned uBlock = (unsigned)(offStart >> pImage->uShiftOffset2Index);
1028 unsigned offRead = (unsigned)offStart & pImage->uBlockMask;
1029
1030 /* Save block size here for speed optimization. */
1031 unsigned cbBlock = getImageBlockSize(&pImage->Header);
1032
1033 /* loop through blocks */
1034 int rc;
1035 for (;;)
1036 {
1037 size_t to_read;
1038 if ((offRead + cbToRead) <= cbBlock)
1039 to_read = cbToRead;
1040 else
1041 to_read = cbBlock - offRead;
1042
1043 if (pDisk->cImages > 1)
1044 {
1045 /* Differencing images are used, handle them. */
1046 pImage = pDisk->pLast;
1047
1048 /* Search for image with allocated block. */
1049 while (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
1050 {
1051 pImage = pImage->pPrev;
1052 if (!pImage)
1053 {
1054 /* Block is not allocated in all images of chain. */
1055 pImage = pDisk->pLast;
1056 break;
1057 }
1058 }
1059 }
1060
1061 rc = vdiReadInBlock(pImage, uBlock, offRead, to_read, pvBuf);
1062
1063 cbToRead -= to_read;
1064 if ( cbToRead == 0
1065 || VBOX_FAILURE(rc))
1066 break;
1067
1068 /* goto next block */
1069 uBlock++;
1070 offRead = 0;
1071 pvBuf = (char *)pvBuf + to_read;
1072 }
1073
1074 return rc;
1075}
1076
1077/**
1078 * internal: fill the whole block with zeroes.
1079 *
1080 * note: block id must be valid, block must be already allocated in file.
1081 * note: if pDisk is NULL, the default buffer size is used
1082 */
1083static int vdiFillBlockByZeroes(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock)
1084{
1085 int rc;
1086
1087 /* seek to start of block in file. */
1088 uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
1089 + (pImage->offStartData + pImage->offStartBlockData);
1090 rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
1091 if (VBOX_FAILURE(rc))
1092 {
1093 Log(("vdiFillBlockByZeroes: seek rc=%Vrc filename=\"%s\" uBlock=%u u64Offset=%llu\n",
1094 rc, pImage->szFilename, uBlock, u64Offset));
1095 return rc;
1096 }
1097
1098 /* alloc tmp zero-filled buffer */
1099 void *pvBuf = RTMemTmpAllocZ(pDisk ? pDisk->cbBuf : VDIDISK_DEFAULT_BUFFER_SIZE);
1100 if (!pvBuf)
1101 return VERR_NO_MEMORY;
1102
1103 unsigned cbFill = getImageBlockSize(&pImage->Header);
1104
1105 /* do loop, because buffer size may be less then block size */
1106 while (cbFill > 0)
1107 {
1108 unsigned to_fill = RT_MIN(cbFill, pDisk ? pDisk->cbBuf : VDIDISK_DEFAULT_BUFFER_SIZE);
1109 rc = RTFileWrite(pImage->File, pvBuf, to_fill, NULL);
1110 if (VBOX_FAILURE(rc))
1111 {
1112 Log(("vdiFillBlockByZeroes: write rc=%Vrc filename=\"%s\" uBlock=%u u64Offset=%llu cbFill=%u to_fill=%u\n",
1113 rc, pImage->szFilename, uBlock, u64Offset, cbFill, to_fill));
1114 break;
1115 }
1116
1117 cbFill -= to_fill;
1118 }
1119
1120 RTMemTmpFree(pvBuf);
1121 return rc;
1122}
1123
1124/**
1125 * internal: write data inside image block.
1126 *
1127 * note: uBlock must be valid, written data must not overlap block bounds.
1128 */
1129static int vdiWriteInBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock, unsigned offWrite, size_t cbToWrite, const void *pvBuf)
1130{
1131 int rc;
1132
1133 /* Check if we can write into file. */
1134 if (pImage->fReadOnly)
1135 {
1136 Log(("vdiWriteInBlock: failed, image \"%s\" is read-only!\n", pImage->szFilename));
1137 return VERR_WRITE_PROTECT;
1138 }
1139
1140 /* This could be optimized a little (not setting it when writing zeroes
1141 * to a zeroed block). Won't buy us much, because it's very unlikely
1142 * that only such zero data block writes occur while the VDI is opened. */
1143 vdiSetModifiedFlag(pImage);
1144
1145 if (!IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
1146 {
1147 if (!pDisk || !pDisk->fHonorZeroWrites)
1148 {
1149 /* If the destination block is unallocated at this point, it's either
1150 * a zero block or a block which hasn't been used so far (which also
1151 * means that it's a zero block. Don't need to write anything to this
1152 * block if the data consists of just zeroes. */
1153 Assert(cbToWrite % 4 == 0);
1154 if (ASMBitFirstSet((volatile void *)pvBuf, (uint32_t)cbToWrite * 8) == -1)
1155 {
1156 pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
1157 return VINF_SUCCESS;
1158 }
1159 }
1160
1161 /* need to allocate a new block in image file */
1162
1163 /* expand file by one block */
1164 uint64_t u64Size = (((uint64_t)(getImageBlocksAllocated(&pImage->Header) + 1)) << pImage->uShiftIndex2Offset)
1165 + pImage->offStartData;
1166 rc = RTFileSetSize(pImage->File, u64Size);
1167 if (VBOX_FAILURE(rc))
1168 {
1169 Log(("vdiWriteInBlock: set size rc=%Vrc filename=\"%s\" uBlock=%u u64Size=%llu\n",
1170 rc, pImage->szFilename, uBlock, u64Size));
1171 return rc;
1172 }
1173
1174 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
1175 pImage->paBlocks[uBlock] = cBlocksAllocated;
1176 setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
1177
1178 if ( pImage->fFlags & VDI_IMAGE_FLAGS_ZERO_EXPAND
1179 || pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
1180 {
1181 /* Fill newly allocated block by zeroes. */
1182
1183 if (offWrite || cbToWrite != getImageBlockSize(&pImage->Header))
1184 {
1185 rc = vdiFillBlockByZeroes(pDisk, pImage, uBlock);
1186 if (VBOX_FAILURE(rc))
1187 return rc;
1188 }
1189 }
1190
1191 rc = vdiUpdateBlockInfo(pImage, uBlock);
1192 if (VBOX_FAILURE(rc))
1193 return rc;
1194 }
1195
1196 /* Now block present in image file, write data inside it. */
1197 uint64_t u64Offset = ((uint64_t)pImage->paBlocks[uBlock] << pImage->uShiftIndex2Offset)
1198 + (pImage->offStartData + pImage->offStartBlockData + offWrite);
1199 rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
1200 if (VBOX_SUCCESS(rc))
1201 {
1202 rc = RTFileWrite(pImage->File, pvBuf, cbToWrite, NULL);
1203 if (VBOX_FAILURE(rc))
1204 Log(("vdiWriteInBlock: write rc=%Vrc filename=\"%s\" uBlock=%u offWrite=%u u64Offset=%llu cbToWrite=%u\n",
1205 rc, pImage->szFilename, uBlock, offWrite, u64Offset, cbToWrite));
1206 }
1207 else
1208 Log(("vdiWriteInBlock: seek rc=%Vrc filename=\"%s\" uBlock=%u offWrite=%u u64Offset=%llu\n",
1209 rc, pImage->szFilename, uBlock, offWrite, u64Offset));
1210
1211 return rc;
1212}
1213
1214/**
1215 * internal: copy data block from one (parent) image to last image.
1216 */
1217static int vdiCopyBlock(PVDIDISK pDisk, PVDIIMAGEDESC pImage, unsigned uBlock)
1218{
1219 Assert(pImage != pDisk->pLast);
1220
1221 if (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
1222 {
1223 /*
1224 * if src block is zero, set dst block to zero too.
1225 */
1226 pDisk->pLast->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
1227 return VINF_SUCCESS;
1228 }
1229
1230 /* alloc tmp buffer */
1231 void *pvBuf = RTMemTmpAlloc(pDisk->cbBuf);
1232 if (!pvBuf)
1233 return VERR_NO_MEMORY;
1234
1235 int rc = VINF_SUCCESS;
1236
1237 unsigned cbCopy = getImageBlockSize(&pImage->Header);
1238 unsigned offCopy = 0;
1239
1240 /* do loop, because buffer size may be less then block size */
1241 while (cbCopy > 0)
1242 {
1243 unsigned to_copy = RT_MIN(cbCopy, pDisk->cbBuf);
1244 rc = vdiReadInBlock(pImage, uBlock, offCopy, to_copy, pvBuf);
1245 if (VBOX_FAILURE(rc))
1246 break;
1247
1248 rc = vdiWriteInBlock(pDisk, pDisk->pLast, uBlock, offCopy, to_copy, pvBuf);
1249 if (VBOX_FAILURE(rc))
1250 break;
1251
1252 cbCopy -= to_copy;
1253 offCopy += to_copy;
1254 }
1255
1256 RTMemTmpFree(pvBuf);
1257 return rc;
1258}
1259
1260/**
1261 * Write data to virtual HDD.
1262 *
1263 * @returns VBox status code.
1264 * @param pDisk Pointer to VDI HDD container.
1265 * @param offStart Offset of first writing byte from start of HDD.
1266 * @param pvBuf Pointer to buffer of writing data.
1267 * @param cbToWrite Number of bytes to write.
1268 */
1269VBOXDDU_DECL(int) VDIDiskWrite(PVDIDISK pDisk, uint64_t offStart, const void *pvBuf, size_t cbToWrite)
1270{
1271 /* sanity check */
1272 Assert(pDisk);
1273 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1274
1275 PVDIIMAGEDESC pImage = pDisk->pLast;
1276 Assert(pImage);
1277
1278 /* Check params. */
1279 if ( offStart + cbToWrite > getImageDiskSize(&pImage->Header)
1280 || cbToWrite == 0)
1281 {
1282 AssertMsgFailed(("offStart=%llu cbToWrite=%u\n", offStart, cbToWrite));
1283 return VERR_INVALID_PARAMETER;
1284 }
1285
1286 /* Calculate starting block number and offset inside it. */
1287 unsigned uBlock = (unsigned)(offStart >> pImage->uShiftOffset2Index);
1288 unsigned offWrite = (unsigned)offStart & pImage->uBlockMask;
1289 unsigned cbBlock = getImageBlockSize(&pImage->Header);
1290
1291 /* loop through blocks */
1292 int rc;
1293 for (;;)
1294 {
1295 size_t to_write;
1296 if (offWrite + cbToWrite <= cbBlock)
1297 to_write = cbToWrite;
1298 else
1299 to_write = cbBlock - offWrite;
1300
1301 /* All callers write less than a VDI block right now (assuming
1302 * default VDI block size). So not worth optimizing for the case
1303 * where a full block is overwritten (no copying required).
1304 * Checking whether a block is all zeroes after the write is too
1305 * expensive (would require reading the rest of the block). */
1306
1307 if (pDisk->cImages > 1)
1308 {
1309 /* Differencing images are used, handle them. */
1310
1311 /* Search for image with allocated block. */
1312 while (pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
1313 {
1314 pImage = pImage->pPrev;
1315 if (!pImage)
1316 {
1317 /* Block is not allocated in all images of chain. */
1318 pImage = pDisk->pLast;
1319 break;
1320 }
1321 }
1322
1323 if (pImage != pDisk->pLast)
1324 {
1325 /* One of parent image has a block data, copy it into last image. */
1326 rc = vdiCopyBlock(pDisk, pImage, uBlock);
1327 if (VBOX_FAILURE(rc))
1328 break;
1329 pImage = pDisk->pLast;
1330 }
1331 }
1332
1333 /* Actually write the data into block. */
1334 rc = vdiWriteInBlock(pDisk, pImage, uBlock, offWrite, to_write, pvBuf);
1335
1336 cbToWrite -= to_write;
1337 if ( cbToWrite == 0
1338 || VBOX_FAILURE(rc))
1339 break;
1340
1341 /* goto next block */
1342 uBlock++;
1343 offWrite = 0;
1344 pvBuf = (char *)pvBuf + to_write;
1345 }
1346
1347 return rc;
1348}
1349
1350/**
1351 * internal: commit one image to another, no changes to header, just
1352 * plain copy operation. Blocks that are not allocated in the source
1353 * image (i.e. inherited by its parent(s)) are not merged.
1354 *
1355 * @param pImageFrom source image
1356 * @param pImageTo target image (will receive all the modifications)
1357 * @param fParentToChild true if the source image is parent of the target one,
1358 * false of the target image is the parent of the source.
1359 * @param pfnProgress progress callback (NULL if not to be used)
1360 * @param pvUser user argument for the progress callback
1361 *
1362 * @note the target image has to be opened read/write
1363 * @note this method does not check whether merging is possible!
1364 */
1365static int vdiMergeImages(PVDIIMAGEDESC pImageFrom, PVDIIMAGEDESC pImageTo, bool fParentToChild,
1366 PFNVMPROGRESS pfnProgress, void *pvUser)
1367{
1368 Assert(pImageFrom);
1369 Assert(pImageTo);
1370
1371 Log(("vdiMergeImages: merging from image \"%s\" to image \"%s\" (fParentToChild=%d)\n",
1372 pImageFrom->szFilename, pImageTo->szFilename, fParentToChild));
1373
1374 /* alloc tmp buffer */
1375 void *pvBuf = RTMemTmpAlloc(VDIDISK_DEFAULT_BUFFER_SIZE);
1376 if (!pvBuf)
1377 return VERR_NO_MEMORY;
1378
1379 int rc = VINF_SUCCESS;
1380
1381 if (!fParentToChild)
1382 {
1383 /*
1384 * Commit the child image to the parent image.
1385 * Child is the source (from), parent is the target (to).
1386 */
1387
1388 unsigned cBlocks = getImageBlocks(&pImageFrom->Header);
1389
1390 for (unsigned uBlock = 0; uBlock < cBlocks; uBlock++)
1391 {
1392 /* only process blocks that are allocated in the source image */
1393 if (pImageFrom->paBlocks[uBlock] != VDI_IMAGE_BLOCK_FREE)
1394 {
1395 /* Found used block in source image, commit it. */
1396 if ( pImageFrom->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
1397 && !IS_VDI_IMAGE_BLOCK_ALLOCATED(pImageTo->paBlocks[uBlock]))
1398 {
1399 /* Block is zero in the source image and not allocated in the target image. */
1400 pImageTo->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
1401 vdiSetModifiedFlag(pImageTo);
1402 }
1403 else
1404 {
1405 /* Block is not zero / allocated in source image. */
1406 unsigned cbCommit = getImageBlockSize(&pImageFrom->Header);
1407 unsigned offCommit = 0;
1408
1409 /* do loop, because buffer size may be less then block size */
1410 while (cbCommit > 0)
1411 {
1412 unsigned cbToCopy = RT_MIN(cbCommit, VDIDISK_DEFAULT_BUFFER_SIZE);
1413
1414 rc = vdiReadInBlock(pImageFrom, uBlock, offCommit, cbToCopy, pvBuf);
1415 if (VBOX_FAILURE(rc))
1416 break;
1417
1418 rc = vdiWriteInBlock(NULL, pImageTo, uBlock, offCommit, cbToCopy, pvBuf);
1419 if (VBOX_FAILURE(rc))
1420 break;
1421
1422 cbCommit -= cbToCopy;
1423 offCommit += cbToCopy;
1424 }
1425 if (VBOX_FAILURE(rc))
1426 break;
1427 }
1428 }
1429
1430 if (pfnProgress)
1431 {
1432 pfnProgress(NULL /* WARNING! pVM=NULL */,
1433 (uBlock * 100) / cBlocks,
1434 pvUser);
1435 /* Note: commiting is non breakable operation, skipping rc here. */
1436 }
1437 }
1438 }
1439 else
1440 {
1441 /*
1442 * Commit the parent image to the child image.
1443 * Parent is the source (from), child is the target (to).
1444 */
1445
1446 unsigned cBlocks = getImageBlocks(&pImageFrom->Header);
1447
1448 for (unsigned uBlock = 0; uBlock < cBlocks; uBlock++)
1449 {
1450 /*
1451 * only process blocks that are allocated or zero in the source image
1452 * and NEITHER allocated NOR zero in the target image
1453 */
1454 if (pImageFrom->paBlocks[uBlock] != VDI_IMAGE_BLOCK_FREE &&
1455 pImageTo->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE)
1456 {
1457 /* Found used block in source image (but unused in target), commit it. */
1458 if ( pImageFrom->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO)
1459 {
1460 /* Block is zero in the source image and not allocated in the target image. */
1461 pImageTo->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
1462 vdiSetModifiedFlag(pImageTo);
1463 }
1464 else
1465 {
1466 /* Block is not zero / allocated in source image. */
1467 unsigned cbCommit = getImageBlockSize(&pImageFrom->Header);
1468 unsigned offCommit = 0;
1469
1470 /* do loop, because buffer size may be less then block size */
1471 while (cbCommit > 0)
1472 {
1473 unsigned cbToCopy = RT_MIN(cbCommit, VDIDISK_DEFAULT_BUFFER_SIZE);
1474
1475 rc = vdiReadInBlock(pImageFrom, uBlock, offCommit, cbToCopy, pvBuf);
1476 if (VBOX_FAILURE(rc))
1477 break;
1478
1479 rc = vdiWriteInBlock(NULL, pImageTo, uBlock, offCommit, cbToCopy, pvBuf);
1480 if (VBOX_FAILURE(rc))
1481 break;
1482
1483 cbCommit -= cbToCopy;
1484 offCommit += cbToCopy;
1485 }
1486 if (VBOX_FAILURE(rc))
1487 break;
1488 }
1489 }
1490
1491 if (pfnProgress)
1492 {
1493 pfnProgress(NULL /* WARNING! pVM=NULL */,
1494 (uBlock * 100) / cBlocks,
1495 pvUser);
1496 /* Note: commiting is non breakable operation, skipping rc here. */
1497 }
1498 }
1499 }
1500
1501 RTMemTmpFree(pvBuf);
1502 return rc;
1503}
1504
1505/**
1506 * internal: commit last image(s) to selected previous image.
1507 * note: all images accessed across this call must be opened in R/W mode.
1508 * @remark Only used by tstVDI.
1509 */
1510static int vdiCommitToImage(PVDIDISK pDisk, PVDIIMAGEDESC pDstImage,
1511 PFNVMPROGRESS pfnProgress, void *pvUser)
1512{
1513 /* sanity check */
1514 Assert(pDisk);
1515 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1516 Assert(pDstImage);
1517
1518 PVDIIMAGEDESC pImage = pDisk->pLast;
1519 Assert(pImage);
1520 Log(("vdiCommitToImage: commiting from image \"%s\" to image \"%s\"\n",
1521 pImage->szFilename, pDstImage->szFilename));
1522 if (pDstImage == pImage)
1523 {
1524 Log(("vdiCommitToImage: attempt to commit to the same image!\n"));
1525 return VERR_VDI_NO_DIFF_IMAGES;
1526 }
1527
1528 /* Scan images for pDstImage. */
1529 while (pImage && pImage != pDstImage)
1530 pImage = pImage->pPrev;
1531 if (!pImage)
1532 {
1533 AssertMsgFailed(("Invalid arguments: pDstImage is not in images chain\n"));
1534 return VERR_INVALID_PARAMETER;
1535 }
1536 pImage = pDisk->pLast;
1537
1538 /* alloc tmp buffer */
1539 void *pvBuf = RTMemTmpAlloc(pDisk->cbBuf);
1540 if (!pvBuf)
1541 return VERR_NO_MEMORY;
1542
1543 int rc = VINF_SUCCESS;
1544 unsigned cBlocks = getImageBlocks(&pImage->Header);
1545
1546 for (unsigned uBlock = 0; uBlock < cBlocks; uBlock++)
1547 {
1548 pImage = pDisk->pLast;
1549
1550 /* Find allocated block to commit. */
1551 while ( pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_FREE
1552 && pImage != pDstImage)
1553 pImage = pImage->pPrev;
1554
1555 if (pImage != pDstImage)
1556 {
1557 /* Found used block in diff image (pImage), commit it. */
1558 if ( pImage->paBlocks[uBlock] == VDI_IMAGE_BLOCK_ZERO
1559 && !IS_VDI_IMAGE_BLOCK_ALLOCATED(pDstImage->paBlocks[uBlock]))
1560 {
1561 /* Block is zero in difference image and not allocated in primary image. */
1562 pDstImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_ZERO;
1563 vdiSetModifiedFlag(pDstImage);
1564 }
1565 else
1566 {
1567 /* Block is not zero / allocated in primary image. */
1568 unsigned cbCommit = getImageBlockSize(&pImage->Header);
1569 unsigned offCommit = 0;
1570
1571 /* do loop, because buffer size may be less then block size */
1572 while (cbCommit > 0)
1573 {
1574 unsigned cbToCopy = RT_MIN(cbCommit, pDisk->cbBuf);
1575
1576 rc = vdiReadInBlock(pImage, uBlock, offCommit, cbToCopy, pvBuf);
1577 if (VBOX_FAILURE(rc))
1578 break;
1579
1580 rc = vdiWriteInBlock(pDisk, pDstImage, uBlock, offCommit, cbToCopy, pvBuf);
1581 if (VBOX_FAILURE(rc))
1582 break;
1583
1584 cbCommit -= cbToCopy;
1585 offCommit += cbToCopy;
1586 }
1587 if (VBOX_FAILURE(rc))
1588 break;
1589 }
1590 pImage->paBlocks[uBlock] = VDI_IMAGE_BLOCK_FREE;
1591 }
1592
1593 if (pfnProgress)
1594 {
1595 pfnProgress(NULL /* WARNING! pVM=NULL */,
1596 (uBlock * 100) / cBlocks,
1597 pvUser);
1598 /* Note: commiting is non breakable operation, skipping rc here. */
1599 }
1600 }
1601
1602 RTMemTmpFree(pvBuf);
1603
1604 /* Go forward and update linkage information. */
1605 for (pImage = pDstImage; pImage; pImage = pImage->pNext)
1606 {
1607 /* generate new last-modified uuid. */
1608 RTUuidCreate(getImageModificationUUID(&pImage->Header));
1609
1610 /* fix up linkage. */
1611 if (pImage != pDstImage)
1612 *getImageParentModificationUUID(&pImage->Header) = *getImageModificationUUID(&pImage->pPrev->Header);
1613
1614 /* reset modified flag. */
1615 pImage->fModified = 0;
1616 }
1617
1618 /* Process committed images - truncate them. */
1619 for (pImage = pDisk->pLast; pImage != pDstImage; pImage = pImage->pPrev)
1620 {
1621 /* note: can't understand how to do error works here? */
1622
1623 setImageBlocksAllocated(&pImage->Header, 0);
1624
1625 /* Truncate file. */
1626 int rc2 = RTFileSetSize(pImage->File, pImage->offStartData);
1627 if (VBOX_FAILURE(rc2))
1628 {
1629 rc = rc2;
1630 Log(("vdiCommitToImage: set size (truncate) rc=%Vrc filename=\"%s\"\n",
1631 rc, pImage->szFilename));
1632 }
1633
1634 /* Save header and blocks array. */
1635 rc2 = vdiUpdateBlocks(pImage);
1636 if (VBOX_FAILURE(rc2))
1637 {
1638 rc = rc2;
1639 Log(("vdiCommitToImage: update blocks and header rc=%Vrc filename=\"%s\"\n",
1640 rc, pImage->szFilename));
1641 }
1642 }
1643
1644 if (pfnProgress)
1645 {
1646 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1647 /* Note: commiting is non breakable operation, skipping rc here. */
1648 }
1649
1650 Log(("vdiCommitToImage: done, rc=%Vrc\n", rc));
1651
1652 return rc;
1653}
1654
1655/**
1656 * Checks if image is available and not broken, returns some useful image parameters if requested.
1657 *
1658 * @returns VBox status code.
1659 * @param pszFilename Name of the image file to check.
1660 * @param puVersion Where to store the version of image. NULL is ok.
1661 * @param penmType Where to store the type of image. NULL is ok.
1662 * @param pcbSize Where to store the size of image in bytes. NULL is ok.
1663 * @param pUuid Where to store the uuid of image creation. NULL is ok.
1664 * @param pParentUuid Where to store the UUID of the parent image. NULL is ok.
1665 * @param pszComment Where to store the comment string of image. NULL is ok.
1666 * @param cbComment The size of pszComment buffer. 0 is ok.
1667 */
1668VBOXDDU_DECL(int) VDICheckImage(const char *pszFilename, unsigned *puVersion, PVDIIMAGETYPE penmType,
1669 uint64_t *pcbSize, PRTUUID pUuid, PRTUUID pParentUuid,
1670 char *pszComment, unsigned cbComment)
1671{
1672 LogFlow(("VDICheckImage:\n"));
1673
1674 /* Check arguments. */
1675 if ( !pszFilename
1676 || *pszFilename == '\0')
1677 {
1678 AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
1679 return VERR_INVALID_PARAMETER;
1680 }
1681
1682 PVDIIMAGEDESC pImage;
1683 int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_READONLY, NULL);
1684 if (VBOX_SUCCESS(rc))
1685 {
1686 Log(("VDICheckImage: filename=\"%s\" version=%08X type=%X cbDisk=%llu uuid={%Vuuid}\n",
1687 pszFilename,
1688 pImage->PreHeader.u32Version,
1689 getImageType(&pImage->Header),
1690 getImageDiskSize(&pImage->Header),
1691 getImageCreationUUID(&pImage->Header)));
1692
1693 if ( pszComment
1694 && cbComment > 0)
1695 {
1696 char *pszTmp = getImageComment(&pImage->Header);
1697 size_t cb = strlen(pszTmp);
1698 if (cbComment > cb)
1699 memcpy(pszComment, pszTmp, cb + 1);
1700 else
1701 rc = VERR_BUFFER_OVERFLOW;
1702 }
1703 if (VBOX_SUCCESS(rc))
1704 {
1705 if (puVersion)
1706 *puVersion = pImage->PreHeader.u32Version;
1707 if (penmType)
1708 *penmType = getImageType(&pImage->Header);
1709 if (pcbSize)
1710 *pcbSize = getImageDiskSize(&pImage->Header);
1711 if (pUuid)
1712 *pUuid = *getImageCreationUUID(&pImage->Header);
1713 if (pParentUuid)
1714 *pParentUuid = *getImageParentUUID(&pImage->Header);
1715 }
1716 vdiCloseImage(pImage);
1717 }
1718
1719 LogFlow(("VDICheckImage: returns %Vrc\n", rc));
1720 return rc;
1721}
1722
1723/**
1724 * Changes an image's comment string.
1725 *
1726 * @returns VBox status code.
1727 * @param pszFilename Name of the image file to operate on.
1728 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
1729 */
1730VBOXDDU_DECL(int) VDISetImageComment(const char *pszFilename, const char *pszComment)
1731{
1732 LogFlow(("VDISetImageComment:\n"));
1733
1734 /*
1735 * Validate arguments.
1736 */
1737 if ( !pszFilename
1738 || *pszFilename == '\0')
1739 {
1740 AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
1741 return VERR_INVALID_PARAMETER;
1742 }
1743
1744 const size_t cchComment = pszComment ? strlen(pszComment) : 0;
1745 if (cchComment >= VDI_IMAGE_COMMENT_SIZE)
1746 {
1747 Log(("VDISetImageComment: pszComment is too long, %d bytes!\n", cchComment));
1748 return VERR_VDI_COMMENT_TOO_LONG;
1749 }
1750
1751 /*
1752 * Open the image for updating.
1753 */
1754 PVDIIMAGEDESC pImage;
1755 int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
1756 if (VBOX_FAILURE(rc))
1757 {
1758 Log(("VDISetImageComment: vdiOpenImage rc=%Vrc filename=\"%s\"!\n", rc, pszFilename));
1759 return rc;
1760 }
1761 if (!pImage->fReadOnly)
1762 {
1763 /* we don't support old style images */
1764 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
1765 {
1766 /*
1767 * Update the comment field, making sure to zero out all of the previous comment.
1768 */
1769 memset(pImage->Header.u.v1.szComment, '\0', VDI_IMAGE_COMMENT_SIZE);
1770 memcpy(pImage->Header.u.v1.szComment, pszComment, cchComment);
1771
1772 /* write out new the header */
1773 rc = vdiUpdateHeader(pImage);
1774 AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
1775 pImage->szFilename, rc));
1776 }
1777 else
1778 {
1779 Log(("VDISetImageComment: Unsupported version!\n"));
1780 rc = VERR_VDI_UNSUPPORTED_VERSION;
1781 }
1782 }
1783 else
1784 {
1785 Log(("VDISetImageComment: image \"%s\" is opened as read-only!\n", pszFilename));
1786 rc = VERR_VDI_IMAGE_READ_ONLY;
1787 }
1788
1789 vdiCloseImage(pImage);
1790 return rc;
1791}
1792
1793/**
1794 * Creates a new base image file.
1795 *
1796 * @returns VBox status code.
1797 * @param pszFilename Name of the creating image file.
1798 * @param enmType Image type, only base image types are acceptable.
1799 * @param cbSize Image size in bytes.
1800 * @param pszComment Pointer to image comment. NULL is ok.
1801 * @param pfnProgress Progress callback. Optional.
1802 * @param pvUser User argument for the progress callback.
1803 */
1804VBOXDDU_DECL(int) VDICreateBaseImage(const char *pszFilename, VDIIMAGETYPE enmType, uint64_t cbSize,
1805 const char *pszComment, PFNVMPROGRESS pfnProgress, void *pvUser)
1806{
1807 LogFlow(("VDICreateBaseImage:\n"));
1808
1809 /* Check arguments. */
1810 if ( !pszFilename
1811 || *pszFilename == '\0'
1812 || (enmType != VDI_IMAGE_TYPE_NORMAL && enmType != VDI_IMAGE_TYPE_FIXED)
1813 || cbSize < VDI_IMAGE_DEFAULT_BLOCK_SIZE)
1814 {
1815 AssertMsgFailed(("Invalid arguments: pszFilename=%p enmType=%x cbSize=%llu\n",
1816 pszFilename, enmType, cbSize));
1817 return VERR_INVALID_PARAMETER;
1818 }
1819
1820 int rc = vdiCreateImage(pszFilename, enmType, VDI_IMAGE_FLAGS_DEFAULT, cbSize, pszComment, NULL,
1821 pfnProgress, pvUser);
1822 LogFlow(("VDICreateBaseImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
1823 return rc;
1824}
1825
1826/**
1827 * Creates a differencing dynamically growing image file for specified parent image.
1828 *
1829 * @returns VBox status code.
1830 * @param pszFilename Name of the creating differencing image file.
1831 * @param pszParent Name of the parent image file. May be base or diff image type.
1832 * @param pszComment Pointer to image comment. NULL is ok.
1833 * @param pfnProgress Progress callback. Optional.
1834 * @param pvUser User argument for the progress callback.
1835 */
1836VBOXDDU_DECL(int) VDICreateDifferenceImage(const char *pszFilename, const char *pszParent,
1837 const char *pszComment, PFNVMPROGRESS pfnProgress,
1838 void *pvUser)
1839{
1840 LogFlow(("VDICreateDifferenceImage:\n"));
1841
1842 /* Check arguments. */
1843 if ( !pszFilename
1844 || *pszFilename == '\0'
1845 || !pszParent
1846 || *pszParent == '\0')
1847 {
1848 AssertMsgFailed(("Invalid arguments: pszFilename=%p pszParent=%p\n",
1849 pszFilename, pszParent));
1850 return VERR_INVALID_PARAMETER;
1851 }
1852
1853 PVDIIMAGEDESC pParent;
1854 int rc = vdiOpenImage(&pParent, pszParent, VDI_OPEN_FLAGS_READONLY, NULL);
1855 if (VBOX_SUCCESS(rc))
1856 {
1857 rc = vdiCreateImage(pszFilename, VDI_IMAGE_TYPE_DIFF, VDI_IMAGE_FLAGS_DEFAULT,
1858 getImageDiskSize(&pParent->Header), pszComment, pParent,
1859 pfnProgress, pvUser);
1860 vdiCloseImage(pParent);
1861 }
1862 LogFlow(("VDICreateDifferenceImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
1863 return rc;
1864}
1865
1866/**
1867 * Deletes an image. Only valid image files can be deleted by this call.
1868 *
1869 * @returns VBox status code.
1870 * @param pszFilename Name of the image file to check.
1871 */
1872VBOXDDU_DECL(int) VDIDeleteImage(const char *pszFilename)
1873{
1874 LogFlow(("VDIDeleteImage:\n"));
1875 /* Check arguments. */
1876 if ( !pszFilename
1877 || *pszFilename == '\0')
1878 {
1879 AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
1880 return VERR_INVALID_PARAMETER;
1881 }
1882
1883 int rc = VDICheckImage(pszFilename, NULL, NULL, NULL, NULL, NULL, NULL, 0);
1884 if (VBOX_SUCCESS(rc))
1885 rc = RTFileDelete(pszFilename);
1886
1887 LogFlow(("VDIDeleteImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
1888 return rc;
1889}
1890
1891/**
1892 * Makes a copy of image file with a new (other) creation uuid.
1893 *
1894 * @returns VBox status code.
1895 * @param pszDstFilename Name of the image file to create.
1896 * @param pszSrcFilename Name of the image file to copy from.
1897 * @param pszComment Pointer to image comment. If NULL specified comment
1898 * will be copied from source image.
1899 * @param pfnProgress Progress callback. Optional.
1900 * @param pvUser User argument for the progress callback.
1901 */
1902VBOXDDU_DECL(int) VDICopyImage(const char *pszDstFilename, const char *pszSrcFilename,
1903 const char *pszComment, PFNVMPROGRESS pfnProgress, void *pvUser)
1904{
1905 LogFlow(("VDICopyImage:\n"));
1906
1907 /* Check arguments. */
1908 if ( !pszDstFilename
1909 || *pszDstFilename == '\0'
1910 || !pszSrcFilename
1911 || *pszSrcFilename == '\0')
1912 {
1913 AssertMsgFailed(("Invalid arguments: pszDstFilename=%p pszSrcFilename=%p\n",
1914 pszDstFilename, pszSrcFilename));
1915 return VERR_INVALID_PARAMETER;
1916 }
1917
1918 /* Special check for comment length. */
1919 if ( pszComment
1920 && strlen(pszComment) >= VDI_IMAGE_COMMENT_SIZE)
1921 {
1922 Log(("VDICopyImage: pszComment is too long, cb=%d\n", strlen(pszComment)));
1923 return VERR_VDI_COMMENT_TOO_LONG;
1924 }
1925
1926 PVDIIMAGEDESC pImage;
1927 int rc = vdiOpenImage(&pImage, pszSrcFilename, VDI_OPEN_FLAGS_READONLY, NULL);
1928 if (VBOX_FAILURE(rc))
1929 {
1930 Log(("VDICopyImage: src image \"%s\" open failed rc=%Vrc\n", pszSrcFilename, rc));
1931 return rc;
1932 }
1933
1934 uint64_t cbFile = pImage->offStartData
1935 + ((uint64_t)getImageBlocksAllocated(&pImage->Header) << pImage->uShiftIndex2Offset);
1936
1937 /* create file */
1938 RTFILE File;
1939 rc = RTFileOpen(&File,
1940 pszDstFilename,
1941 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL | RTFILE_O_NOT_CONTENT_INDEXED);
1942 if (VBOX_SUCCESS(rc))
1943 {
1944 /* lock new image exclusively to close any wrong access by VDI API calls. */
1945 rc = RTFileLock(File, RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY, 0, cbFile);
1946 if (VBOX_SUCCESS(rc))
1947 {
1948 /* Set the size of a new file. */
1949 rc = RTFileSetSize(File, cbFile);
1950 if (VBOX_SUCCESS(rc))
1951 {
1952 /* A dirty trick - use original image data to fill the new image. */
1953 RTFILE oldFileHandle = pImage->File;
1954 pImage->File = File;
1955 pImage->fReadOnly = false;
1956
1957 /* generate a new image creation uuid. */
1958 RTUuidCreate(getImageCreationUUID(&pImage->Header));
1959 /* generate a new image last-modified uuid. */
1960 RTUuidCreate(getImageModificationUUID(&pImage->Header));
1961 /* set image comment, if present. */
1962 if (pszComment)
1963 strncpy(getImageComment(&pImage->Header), pszComment, VDI_IMAGE_COMMENT_SIZE);
1964
1965 /* Write the pre-header to new image. */
1966 rc = RTFileSeek(pImage->File, 0, RTFILE_SEEK_BEGIN, NULL);
1967 if (VBOX_SUCCESS(rc))
1968 rc = RTFileWrite(pImage->File,
1969 &pImage->PreHeader,
1970 sizeof(pImage->PreHeader),
1971 NULL);
1972
1973 /* Write the header and the blocks array to new image. */
1974 if (VBOX_SUCCESS(rc))
1975 rc = vdiUpdateBlocks(pImage);
1976
1977 pImage->File = oldFileHandle;
1978 pImage->fReadOnly = true;
1979
1980 /* Seek to the data start in both images. */
1981 if (VBOX_SUCCESS(rc))
1982 rc = RTFileSeek(pImage->File,
1983 pImage->offStartData,
1984 RTFILE_SEEK_BEGIN,
1985 NULL);
1986 if (VBOX_SUCCESS(rc))
1987 rc = RTFileSeek(File,
1988 pImage->offStartData,
1989 RTFILE_SEEK_BEGIN,
1990 NULL);
1991
1992 if (VBOX_SUCCESS(rc))
1993 {
1994 /* alloc tmp buffer */
1995 void *pvBuf = RTMemTmpAlloc(VDIDISK_DEFAULT_BUFFER_SIZE);
1996 if (pvBuf)
1997 {
1998 /* Main copy loop. */
1999 uint64_t cbData = cbFile - pImage->offStartData;
2000 unsigned cBlocks = (unsigned)(cbData / VDIDISK_DEFAULT_BUFFER_SIZE);
2001 unsigned c = 0;
2002
2003 while (cbData)
2004 {
2005 unsigned cbToCopy = (unsigned)RT_MIN(cbData, VDIDISK_DEFAULT_BUFFER_SIZE);
2006
2007 /* Read. */
2008 rc = RTFileRead(pImage->File, pvBuf, cbToCopy, NULL);
2009 if (VBOX_FAILURE(rc))
2010 break;
2011
2012 /* Write. */
2013 rc = RTFileWrite(File, pvBuf, cbToCopy, NULL);
2014 if (VBOX_FAILURE(rc))
2015 break;
2016
2017 if (pfnProgress)
2018 {
2019 c++;
2020 rc = pfnProgress(NULL /* WARNING! pVM=NULL */,
2021 (c * 100) / cBlocks,
2022 pvUser);
2023 if (VBOX_FAILURE(rc))
2024 break;
2025 }
2026 cbData -= cbToCopy;
2027 }
2028
2029 RTMemTmpFree(pvBuf);
2030 }
2031 else
2032 rc = VERR_NO_MEMORY;
2033 }
2034 }
2035
2036 RTFileUnlock(File, 0, cbFile);
2037 }
2038
2039 RTFileClose(File);
2040
2041 if (VBOX_FAILURE(rc))
2042 RTFileDelete(pszDstFilename);
2043
2044 if (pfnProgress)
2045 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
2046 }
2047
2048 vdiCloseImage(pImage);
2049
2050 LogFlow(("VDICopyImage: returns %Vrc for pszSrcFilename=\"%s\" pszDstFilename=\"%s\"\n",
2051 rc, pszSrcFilename, pszDstFilename));
2052 return rc;
2053}
2054
2055/**
2056 * Shrinks growing image file by removing zeroed data blocks.
2057 *
2058 * @returns VBox status code.
2059 * @param pszFilename Name of the image file to shrink.
2060 * @param pfnProgress Progress callback. Optional.
2061 * @param pvUser User argument for the progress callback.
2062 */
2063VBOXDDU_DECL(int) VDIShrinkImage(const char *pszFilename, PFNVMPROGRESS pfnProgress, void *pvUser)
2064{
2065 LogFlow(("VDIShrinkImage:\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_NORMAL, NULL);
2077 if (VBOX_FAILURE(rc))
2078 {
2079 Log(("VDIShrinkImage: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
2080 return rc;
2081 }
2082 if (pImage->fReadOnly)
2083 {
2084 Log(("VDIShrinkImage: image \"%s\" is opened as read-only!\n", pszFilename));
2085 vdiCloseImage(pImage);
2086 return VERR_VDI_IMAGE_READ_ONLY;
2087 }
2088
2089 /* Do debug dump. */
2090 vdiDumpImage(pImage);
2091
2092 /* Working data. */
2093 unsigned cbBlock = getImageBlockSize(&pImage->Header);
2094 unsigned cBlocks = getImageBlocks(&pImage->Header);
2095 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
2096
2097 uint64_t cbFile;
2098 rc = RTFileGetSize(pImage->File, &cbFile);
2099 if (VBOX_FAILURE(rc))
2100 {
2101 Log(("VDIShrinkImage: RTFileGetSize rc=%Vrc for file=\"%s\"\n", rc, pszFilename));
2102 vdiCloseImage(pImage);
2103 return rc;
2104 }
2105
2106 uint64_t cbData = cbFile - pImage->offStartData;
2107 unsigned cBlocksAllocated2 = (unsigned)(cbData >> pImage->uShiftIndex2Offset);
2108 if (cbData != (uint64_t)cBlocksAllocated << pImage->uShiftIndex2Offset)
2109 Log(("VDIShrinkImage: invalid image file length, cbBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
2110 cbBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
2111
2112 /* Allocate second blocks array for back resolving. */
2113 PVDIIMAGEBLOCKPOINTER paBlocks2 =
2114 (PVDIIMAGEBLOCKPOINTER)RTMemTmpAlloc(sizeof(VDIIMAGEBLOCKPOINTER) * cBlocks);
2115 if (!paBlocks2)
2116 {
2117 Log(("VDIShrinkImage: failed to allocate paBlocks2 buffer (%u bytes)\n", sizeof(VDIIMAGEBLOCKPOINTER) * cBlocks));
2118 vdiCloseImage(pImage);
2119 return VERR_NO_MEMORY;
2120 }
2121
2122 /* Init second blocks array. */
2123 for (unsigned n = 0; n < cBlocks; n++)
2124 paBlocks2[n] = VDI_IMAGE_BLOCK_FREE;
2125
2126 /* Fill second blocks array, check for allocational errors. */
2127 for (unsigned n = 0; n < cBlocks; n++)
2128 {
2129 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[n]))
2130 {
2131 unsigned uBlock = pImage->paBlocks[n];
2132 if (uBlock < cBlocksAllocated2)
2133 {
2134 if (paBlocks2[uBlock] == VDI_IMAGE_BLOCK_FREE)
2135 paBlocks2[uBlock] = n;
2136 else
2137 {
2138 Log(("VDIShrinkImage: block n=%u -> uBlock=%u is already in use!\n", n, uBlock));
2139 /* free second link to block. */
2140 pImage->paBlocks[n] = VDI_IMAGE_BLOCK_FREE;
2141 }
2142 }
2143 else
2144 {
2145 Log(("VDIShrinkImage: block n=%u -> uBlock=%u is out of blocks range! (cbBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu)\n",
2146 n, uBlock, cbBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
2147 /* free link to invalid block. */
2148 pImage->paBlocks[n] = VDI_IMAGE_BLOCK_FREE;
2149 }
2150 }
2151 }
2152
2153 /* Allocate a working buffer for one block. */
2154 void *pvBuf = RTMemTmpAlloc(cbBlock);
2155 if (pvBuf)
2156 {
2157 /* Main voodoo loop, search holes and fill it. */
2158 unsigned uBlockWrite = 0;
2159 for (unsigned uBlock = 0; uBlock < cBlocksAllocated2; uBlock++)
2160 {
2161 if (paBlocks2[uBlock] != VDI_IMAGE_BLOCK_FREE)
2162 {
2163 /* Read the block from file and check for zeroes. */
2164 uint64_t u64Offset = ((uint64_t)uBlock << pImage->uShiftIndex2Offset)
2165 + (pImage->offStartData + pImage->offStartBlockData);
2166 rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
2167 if (VBOX_FAILURE(rc))
2168 {
2169 Log(("VDIShrinkImage: seek rc=%Vrc filename=\"%s\" uBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
2170 rc, pImage->szFilename, uBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
2171 break;
2172 }
2173 rc = RTFileRead(pImage->File, pvBuf, cbBlock, NULL);
2174 if (VBOX_FAILURE(rc))
2175 {
2176 Log(("VDIShrinkImage: read rc=%Vrc filename=\"%s\" cbBlock=%u uBlock=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
2177 rc, pImage->szFilename, cbBlock, uBlock, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
2178 break;
2179 }
2180
2181 /* Check block for data. */
2182 Assert(cbBlock % 4 == 0);
2183 if (ASMBitFirstSet(pvBuf, cbBlock * 8) != -1)
2184 {
2185 /* Block has a data, may be it must be moved. */
2186 if (uBlockWrite < uBlock)
2187 {
2188 /* Move the block. */
2189 u64Offset = ((uint64_t)uBlockWrite << pImage->uShiftIndex2Offset)
2190 + (pImage->offStartData + pImage->offStartBlockData);
2191 rc = RTFileSeek(pImage->File, u64Offset, RTFILE_SEEK_BEGIN, NULL);
2192 if (VBOX_FAILURE(rc))
2193 {
2194 Log(("VDIShrinkImage: seek(2) rc=%Vrc filename=\"%s\" uBlockWrite=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
2195 rc, pImage->szFilename, uBlockWrite, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
2196 break;
2197 }
2198 rc = RTFileWrite(pImage->File, pvBuf, cbBlock, NULL);
2199 if (VBOX_FAILURE(rc))
2200 {
2201 Log(("VDIShrinkImage: write rc=%Vrc filename=\"%s\" cbBlock=%u uBlockWrite=%u cBlocks=%u cBlocksAllocated=%u cBlocksAllocated2=%u cbData=%llu\n",
2202 rc, pImage->szFilename, cbBlock, uBlockWrite, cBlocks, cBlocksAllocated, cBlocksAllocated2, cbData));
2203 break;
2204 }
2205 }
2206 /* Fix the block pointer. */
2207 pImage->paBlocks[paBlocks2[uBlock]] = uBlockWrite;
2208 uBlockWrite++;
2209 }
2210 else
2211 {
2212 Log(("VDIShrinkImage: found a zeroed block, uBlock=%u\n", uBlock));
2213
2214 /* Fix the block pointer. */
2215 pImage->paBlocks[paBlocks2[uBlock]] = VDI_IMAGE_BLOCK_ZERO;
2216 }
2217 }
2218 else
2219 Log(("VDIShrinkImage: found an unused block, uBlock=%u\n", uBlock));
2220
2221 if (pfnProgress)
2222 {
2223 pfnProgress(NULL /* WARNING! pVM=NULL */,
2224 (uBlock * 100) / cBlocksAllocated2,
2225 pvUser);
2226 /* Shrink is unbreakable operation! */
2227 }
2228 }
2229
2230 RTMemTmpFree(pvBuf);
2231
2232 if ( VBOX_SUCCESS(rc)
2233 && uBlockWrite < cBlocksAllocated2)
2234 {
2235 /* File size must be shrinked. */
2236 Log(("VDIShrinkImage: shrinking file size from %llu to %llu bytes\n",
2237 cbFile,
2238 pImage->offStartData + ((uint64_t)uBlockWrite << pImage->uShiftIndex2Offset)));
2239 rc = RTFileSetSize(pImage->File,
2240 pImage->offStartData + ((uint64_t)uBlockWrite << pImage->uShiftIndex2Offset));
2241 if (VBOX_FAILURE(rc))
2242 Log(("VDIShrinkImage: RTFileSetSize rc=%Vrc\n", rc));
2243 }
2244 cBlocksAllocated2 = uBlockWrite;
2245 }
2246 else
2247 {
2248 Log(("VDIShrinkImage: failed to allocate working buffer (%u bytes)\n", cbBlock));
2249 rc = VERR_NO_MEMORY;
2250 }
2251
2252 /* Save header and blocks array. */
2253 if (VBOX_SUCCESS(rc))
2254 {
2255 setImageBlocksAllocated(&pImage->Header, cBlocksAllocated2);
2256 rc = vdiUpdateBlocks(pImage);
2257 if (pfnProgress)
2258 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
2259 }
2260
2261 /* Do debug dump. */
2262 vdiDumpImage(pImage);
2263
2264 /* Clean up. */
2265 RTMemTmpFree(paBlocks2);
2266 vdiCloseImage(pImage);
2267
2268 LogFlow(("VDIShrinkImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
2269 return rc;
2270}
2271
2272/**
2273 * Converts image file from older VDI formats to current one.
2274 *
2275 * @returns VBox status code.
2276 * @param pszFilename Name of the image file to convert.
2277 * @param pfnProgress Progress callback. Optional.
2278 * @param pvUser User argument for the progress callback.
2279 * @remark Only used by vditool
2280 */
2281VBOXDDU_DECL(int) VDIConvertImage(const char *pszFilename, PFNVMPROGRESS pfnProgress, void *pvUser)
2282{
2283 LogFlow(("VDIConvertImage:\n"));
2284
2285 /* Check arguments. */
2286 if ( !pszFilename
2287 || *pszFilename == '\0')
2288 {
2289 AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
2290 return VERR_INVALID_PARAMETER;
2291 }
2292
2293 PVDIIMAGEDESC pImage;
2294 int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
2295 if (VBOX_FAILURE(rc))
2296 {
2297 Log(("VDIConvertImage: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
2298 return rc;
2299 }
2300
2301 VDIHEADER Header = {0};
2302 int off;
2303 uint64_t cbFile;
2304 uint64_t cbData;
2305
2306 if (pImage->fReadOnly)
2307 {
2308 Log(("VDIConvertImage: image \"%s\" is opened as read-only!\n", pszFilename));
2309 rc = VERR_VDI_IMAGE_READ_ONLY;
2310 goto l_conversion_failed;
2311 }
2312
2313 if (pImage->PreHeader.u32Version != 0x00000002)
2314 {
2315 Log(("VDIConvertImage: unsupported version=%08X filename=\"%s\"\n",
2316 pImage->PreHeader.u32Version, pszFilename));
2317 rc = VERR_VDI_UNSUPPORTED_VERSION;
2318 goto l_conversion_failed;
2319 }
2320
2321 /* Build new version header from old one. */
2322 vdiInitHeader(&Header,
2323 getImageType(&pImage->Header),
2324 VDI_IMAGE_FLAGS_DEFAULT, /* Safety issue: Always use default flags. */
2325 getImageComment(&pImage->Header),
2326 getImageDiskSize(&pImage->Header),
2327 getImageBlockSize(&pImage->Header),
2328 0);
2329 setImageBlocksAllocated(&Header, getImageBlocksAllocated(&pImage->Header));
2330 /* Set both the LCHSGeometry and LegacyGeometry. If they're wrong they
2331 * will be fixed later when the image is used. */
2332 if (getImageLCHSGeometry(&pImage->Header))
2333 {
2334 Header.u.v1.LegacyGeometry = *getImageLCHSGeometry(&pImage->Header);
2335 Header.u.v1plus.LCHSGeometry = *getImageLCHSGeometry(&pImage->Header);
2336 }
2337 *getImageCreationUUID(&Header) = *getImageCreationUUID(&pImage->Header);
2338 *getImageModificationUUID(&Header) = *getImageModificationUUID(&pImage->Header);
2339
2340 /* Calc data offset. */
2341 off = getImageDataOffset(&Header) - getImageDataOffset(&pImage->Header);
2342 if (off <= 0)
2343 {
2344 rc = VERR_VDI_INVALID_HEADER;
2345 goto l_conversion_failed;
2346 }
2347
2348 rc = RTFileGetSize(pImage->File, &cbFile);
2349 if (VBOX_FAILURE(rc))
2350 goto l_conversion_failed;
2351
2352 /* Check file size. */
2353 cbData = cbFile - getImageDataOffset(&pImage->Header);
2354 if (cbData != (uint64_t)getImageBlocksAllocated(&pImage->Header) << pImage->uShiftIndex2Offset)
2355 {
2356 AssertMsgFailed(("Invalid file size, broken image?\n"));
2357 rc = VERR_VDI_INVALID_HEADER;
2358 goto l_conversion_failed;
2359 }
2360
2361 /* Expand file. */
2362 rc = RTFileSetSize(pImage->File, cbFile + off);
2363 if (VBOX_FAILURE(rc))
2364 goto l_conversion_failed;
2365
2366 if (cbData > 0)
2367 {
2368 /* Calc current file position to move data from. */
2369 uint64_t offFile;
2370 if (cbData > VDIDISK_DEFAULT_BUFFER_SIZE)
2371 offFile = cbFile - VDIDISK_DEFAULT_BUFFER_SIZE;
2372 else
2373 offFile = getImageDataOffset(&pImage->Header);
2374
2375 unsigned cMoves = (unsigned)(cbData / VDIDISK_DEFAULT_BUFFER_SIZE);
2376 unsigned c = 0;
2377
2378 /* alloc tmp buffer */
2379 void *pvBuf = RTMemTmpAlloc(VDIDISK_DEFAULT_BUFFER_SIZE);
2380 if (pvBuf)
2381 {
2382 /* Move data. */
2383 for (;;)
2384 {
2385 unsigned cbToMove = (unsigned)RT_MIN(cbData, VDIDISK_DEFAULT_BUFFER_SIZE);
2386
2387 /* Read. */
2388 rc = RTFileSeek(pImage->File, offFile, RTFILE_SEEK_BEGIN, NULL);
2389 if (VBOX_FAILURE(rc))
2390 break;
2391 rc = RTFileRead(pImage->File, pvBuf, cbToMove, NULL);
2392 if (VBOX_FAILURE(rc))
2393 break;
2394
2395 /* Write. */
2396 rc = RTFileSeek(pImage->File, offFile + off, RTFILE_SEEK_BEGIN, NULL);
2397 if (VBOX_FAILURE(rc))
2398 break;
2399 rc = RTFileWrite(pImage->File, pvBuf, cbToMove, NULL);
2400 if (VBOX_FAILURE(rc))
2401 break;
2402
2403 if (pfnProgress)
2404 {
2405 c++;
2406 pfnProgress(NULL /* WARNING! pVM=NULL */,
2407 (c * 100) / cMoves,
2408 pvUser);
2409 /* Note: conversion is non breakable operation, skipping rc here. */
2410 }
2411
2412 cbData -= cbToMove;
2413 if (cbData == 0)
2414 break;
2415
2416 if (cbData > VDIDISK_DEFAULT_BUFFER_SIZE)
2417 offFile -= VDIDISK_DEFAULT_BUFFER_SIZE;
2418 else
2419 offFile = getImageDataOffset(&pImage->Header);
2420 }
2421
2422 /* Fill the beginning of file with zeroes to wipe out old headers etc. */
2423 if (VBOX_SUCCESS(rc))
2424 {
2425 Assert(offFile + off <= VDIDISK_DEFAULT_BUFFER_SIZE);
2426 rc = RTFileSeek(pImage->File, 0, RTFILE_SEEK_BEGIN, NULL);
2427 if (VBOX_SUCCESS(rc))
2428 {
2429 memset(pvBuf, 0, (unsigned)offFile + off);
2430 rc = RTFileWrite(pImage->File, pvBuf, (unsigned)offFile + off, NULL);
2431 }
2432 }
2433
2434 RTMemTmpFree(pvBuf);
2435 }
2436 else
2437 rc = VERR_NO_MEMORY;
2438
2439 if (VBOX_FAILURE(rc))
2440 goto l_conversion_failed;
2441 }
2442
2443 if (pfnProgress)
2444 {
2445 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
2446 /* Note: conversion is non breakable operation, skipping rc here. */
2447 }
2448
2449 /* Data moved, now we need to save new pre header, header and blocks array. */
2450
2451 vdiInitPreHeader(&pImage->PreHeader);
2452 pImage->Header = Header;
2453
2454 /* Setup image parameters by header. */
2455 vdiSetupImageDesc(pImage);
2456
2457 /* Write pre-header. */
2458 rc = RTFileSeek(pImage->File, 0, RTFILE_SEEK_BEGIN, NULL);
2459 if (VBOX_FAILURE(rc))
2460 goto l_conversion_failed;
2461 rc = RTFileWrite(pImage->File, &pImage->PreHeader, sizeof(pImage->PreHeader), NULL);
2462 if (VBOX_FAILURE(rc))
2463 goto l_conversion_failed;
2464
2465 /* Write header and blocks array. */
2466 rc = vdiUpdateBlocks(pImage);
2467
2468l_conversion_failed:
2469 vdiCloseImage(pImage);
2470
2471 LogFlow(("VDIConvertImage: returns %Vrc for filename=\"%s\"\n", rc, pszFilename));
2472 return rc;
2473}
2474
2475/**
2476 * Queries the image's UUID and parent UUIDs.
2477 *
2478 * @returns VBox status code.
2479 * @param pszFilename Name of the image file to operate on.
2480 * @param pUuid Where to store image UUID (can be NULL).
2481 * @param pModificationUuid Where to store modification UUID (can be NULL).
2482 * @param pParentUuuid Where to store parent UUID (can be NULL).
2483 * @param pParentModificationUuid Where to store parent modification UUID (can be NULL).
2484 */
2485VBOXDDU_DECL(int) VDIGetImageUUIDs(const char *pszFilename,
2486 PRTUUID pUuid, PRTUUID pModificationUuid,
2487 PRTUUID pParentUuid, PRTUUID pParentModificationUuid)
2488{
2489 LogFlow(("VDIGetImageUUIDs:\n"));
2490
2491 /* Check arguments. */
2492 if ( !pszFilename
2493 || *pszFilename == '\0')
2494 {
2495 AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
2496 return VERR_INVALID_PARAMETER;
2497 }
2498
2499 /*
2500 * Try open the specified image.
2501 */
2502 PVDIIMAGEDESC pImage;
2503 int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
2504 if (VBOX_FAILURE(rc))
2505 {
2506 Log(("VDIGetImageUUIDs: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
2507 return rc;
2508 }
2509
2510 /*
2511 * Query data.
2512 */
2513 if (pUuid)
2514 {
2515 PCRTUUID pTmpUuid = getImageCreationUUID(&pImage->Header);
2516 if (pTmpUuid)
2517 *pUuid = *pTmpUuid;
2518 else
2519 RTUuidClear(pUuid);
2520 }
2521 if (pModificationUuid)
2522 {
2523 PCRTUUID pTmpUuid = getImageModificationUUID(&pImage->Header);
2524 if (pTmpUuid)
2525 *pModificationUuid = *pTmpUuid;
2526 else
2527 RTUuidClear(pModificationUuid);
2528 }
2529 if (pParentUuid)
2530 {
2531 PCRTUUID pTmpUuid = getImageParentUUID(&pImage->Header);
2532 if (pTmpUuid)
2533 *pParentUuid = *pTmpUuid;
2534 else
2535 RTUuidClear(pParentUuid);
2536 }
2537 if (pParentModificationUuid)
2538 {
2539 PCRTUUID pTmpUuid = getImageParentModificationUUID(&pImage->Header);
2540 if (pTmpUuid)
2541 *pParentModificationUuid = *pTmpUuid;
2542 else
2543 RTUuidClear(pParentModificationUuid);
2544 }
2545
2546 /*
2547 * Close the image.
2548 */
2549 vdiCloseImage(pImage);
2550
2551 return VINF_SUCCESS;
2552}
2553
2554/**
2555 * Changes the image's UUID and parent UUIDs.
2556 *
2557 * @returns VBox status code.
2558 * @param pszFilename Name of the image file to operate on.
2559 * @param pUuid Optional parameter, new UUID of the image.
2560 * @param pModificationUuid Optional parameter, new modification UUID of the image.
2561 * @param pParentUuuid Optional parameter, new parent UUID of the image.
2562 * @param pParentModificationUuid Optional parameter, new parent modification UUID of the image.
2563 */
2564VBOXDDU_DECL(int) VDISetImageUUIDs(const char *pszFilename,
2565 PCRTUUID pUuid, PCRTUUID pModificationUuid,
2566 PCRTUUID pParentUuid, PCRTUUID pParentModificationUuid)
2567{
2568 LogFlow(("VDISetImageUUIDs:\n"));
2569
2570 /* Check arguments. */
2571 if ( !pszFilename
2572 || *pszFilename == '\0')
2573 {
2574 AssertMsgFailed(("Invalid arguments: pszFilename=%p\n", pszFilename));
2575 return VERR_INVALID_PARAMETER;
2576 }
2577
2578 PVDIIMAGEDESC pImage;
2579 int rc = vdiOpenImage(&pImage, pszFilename, VDI_OPEN_FLAGS_NORMAL, NULL);
2580 if (VBOX_FAILURE(rc))
2581 {
2582 Log(("VDISetImageUUIDs: vdiOpenImage rc=%Vrc filename=\"%s\"\n", rc, pszFilename));
2583 return rc;
2584 }
2585 if (!pImage->fReadOnly)
2586 {
2587 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) == 1)
2588 {
2589 if (pUuid)
2590 pImage->Header.u.v1.uuidCreate = *pUuid;
2591
2592 if (pModificationUuid)
2593 pImage->Header.u.v1.uuidModify = *pModificationUuid;
2594
2595 if (pParentUuid)
2596 pImage->Header.u.v1.uuidLinkage = *pParentUuid;
2597
2598 if (pParentModificationUuid)
2599 pImage->Header.u.v1.uuidParentModify = *pParentModificationUuid;
2600
2601 /* write out new header */
2602 rc = vdiUpdateHeader(pImage);
2603 AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
2604 pImage->szFilename, rc));
2605 }
2606 /* Make it possible to clone old VDIs. */
2607 else if ( GET_MAJOR_HEADER_VERSION(&pImage->Header) == 0
2608 && !pParentUuid
2609 && !pParentModificationUuid)
2610 {
2611 if (pUuid)
2612 pImage->Header.u.v0.uuidCreate = *pUuid;
2613
2614 if (pModificationUuid)
2615 pImage->Header.u.v0.uuidModify = *pModificationUuid;
2616
2617 /* write out new header */
2618 rc = vdiUpdateHeader(pImage);
2619 AssertMsgRC(rc, ("vdiUpdateHeader() failed, filename=\"%s\", rc=%Vrc\n",
2620 pImage->szFilename, rc));
2621 }
2622 else
2623 {
2624 Log(("VDISetImageUUIDs: Version is not supported!\n"));
2625 rc = VERR_VDI_UNSUPPORTED_VERSION;
2626 }
2627 }
2628 else
2629 {
2630 Log(("VDISetImageUUIDs: image \"%s\" is opened as read-only!\n", pszFilename));
2631 rc = VERR_VDI_IMAGE_READ_ONLY;
2632 }
2633
2634 vdiCloseImage(pImage);
2635 return rc;
2636}
2637
2638/**
2639 * Merges two images having a parent/child relationship (both directions).
2640 *
2641 * @returns VBox status code.
2642 * @param pszFilenameFrom Name of the image file to merge from.
2643 * @param pszFilenameTo Name of the image file to merge into.
2644 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
2645 * @param pvUser User argument for the progress callback.
2646 */
2647VBOXDDU_DECL(int) VDIMergeImage(const char *pszFilenameFrom, const char *pszFilenameTo,
2648 PFNVMPROGRESS pfnProgress, void *pvUser)
2649{
2650 LogFlow(("VDIMergeImage:\n"));
2651
2652 /* Check arguments. */
2653 if ( !pszFilenameFrom
2654 || *pszFilenameFrom == '\0'
2655 || !pszFilenameTo
2656 || *pszFilenameTo == '\0')
2657 {
2658 AssertMsgFailed(("Invalid arguments: pszFilenameFrom=%p, pszFilenameTo=%p\n", pszFilenameFrom, pszFilenameTo));
2659 return VERR_INVALID_PARAMETER;
2660 }
2661
2662 PVDIIMAGEDESC pImageFrom;
2663 int rc = vdiOpenImage(&pImageFrom, pszFilenameFrom, VDI_OPEN_FLAGS_READONLY, NULL);
2664 if (VBOX_FAILURE(rc))
2665 {
2666 Log(("VDIMergeImage: vdiOpenImage rc=%Vrc pstFilenameFrom=\"%s\"\n", rc, pszFilenameFrom));
2667 return rc;
2668 }
2669
2670 PVDIIMAGEDESC pImageTo;
2671 rc = vdiOpenImage(&pImageTo, pszFilenameTo, VDI_OPEN_FLAGS_NORMAL, NULL);
2672 if (VBOX_FAILURE(rc))
2673 {
2674 Log(("VDIMergeImage: vdiOpenImage rc=%Vrc pszFilenameTo=\"%s\"\n", rc, pszFilenameTo));
2675 vdiCloseImage(pImageFrom);
2676 return rc;
2677 }
2678 if (pImageTo->fReadOnly)
2679 {
2680 Log(("VDIMergeImage: image \"%s\" is opened as read-only!\n", pszFilenameTo));
2681 vdiCloseImage(pImageFrom);
2682 vdiCloseImage(pImageTo);
2683 return VERR_VDI_IMAGE_READ_ONLY;
2684 }
2685
2686 /*
2687 * when merging, we should not update the modification uuid of the target
2688 * image, because from the point of view of its children, it hasn't been
2689 * logically changed after the successful merge.
2690 */
2691 vdiDisableLastModifiedUpdate(pImageTo);
2692
2693 /*
2694 * Check in which direction we merge
2695 */
2696
2697 bool bParentToChild = false;
2698 if ( getImageParentUUID(&pImageFrom->Header)
2699 && !RTUuidCompare(getImageParentUUID(&pImageFrom->Header),
2700 getImageCreationUUID(&pImageTo->Header))
2701 && !RTUuidCompare(getImageParentModificationUUID(&pImageFrom->Header),
2702 getImageModificationUUID(&pImageTo->Header)))
2703 {
2704 /* we merge from a child to its parent */
2705 }
2706 else
2707 if ( getImageParentUUID(&pImageTo->Header)
2708 && !RTUuidCompare(getImageParentUUID(&pImageTo->Header),
2709 getImageCreationUUID(&pImageFrom->Header))
2710 && !RTUuidCompare(getImageParentModificationUUID(&pImageTo->Header),
2711 getImageModificationUUID(&pImageFrom->Header)))
2712 {
2713 /* we merge from a parent to its child */
2714 bParentToChild = true;
2715 }
2716 else
2717 {
2718 /* the images are not related, we can't merge! */
2719 Log(("VDIMergeImages: images do not have a parent/child or child/parent relationship!\n"));
2720 rc = VERR_VDI_IMAGES_UUID_MISMATCH;
2721 }
2722
2723 rc = vdiMergeImages(pImageFrom, pImageTo, bParentToChild, pfnProgress, pvUser);
2724
2725 if (pfnProgress)
2726 {
2727 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
2728 /* Note: commiting is non breakable operation, skipping rc here. */
2729 }
2730
2731 /* cleanup */
2732 vdiCloseImage(pImageFrom);
2733 vdiCloseImage(pImageTo);
2734
2735 Log(("VDIMergeImage: done, returning with rc = %Vrc\n", rc));
2736 return rc;
2737}
2738
2739
2740/**
2741 * Initialize the VDIDISK structure.
2742 */
2743void vdiInitVDIDisk(PVDIDISK pDisk)
2744{
2745 Assert(pDisk);
2746 pDisk->u32Signature = VDIDISK_SIGNATURE;
2747 pDisk->cImages = 0;
2748 pDisk->pBase = NULL;
2749 pDisk->pLast = NULL;
2750 pDisk->cbBlock = VDI_IMAGE_DEFAULT_BLOCK_SIZE;
2751 pDisk->cbBuf = VDIDISK_DEFAULT_BUFFER_SIZE;
2752 pDisk->fHonorZeroWrites = false;
2753}
2754
2755/**
2756 * internal: add image structure to the end of images list.
2757 */
2758static void vdiAddImageToList(PVDIDISK pDisk, PVDIIMAGEDESC pImage)
2759{
2760 pImage->pPrev = NULL;
2761 pImage->pNext = NULL;
2762
2763 if (pDisk->pBase)
2764 {
2765 Assert(pDisk->cImages > 0);
2766 pImage->pPrev = pDisk->pLast;
2767 pDisk->pLast->pNext = pImage;
2768 pDisk->pLast = pImage;
2769 }
2770 else
2771 {
2772 Assert(pDisk->cImages == 0);
2773 pDisk->pBase = pImage;
2774 pDisk->pLast = pImage;
2775 }
2776
2777 pDisk->cImages++;
2778}
2779
2780/**
2781 * internal: remove image structure from the images list.
2782 */
2783static void vdiRemoveImageFromList(PVDIDISK pDisk, PVDIIMAGEDESC pImage)
2784{
2785 Assert(pDisk->cImages > 0);
2786
2787 if (pImage->pPrev)
2788 pImage->pPrev->pNext = pImage->pNext;
2789 else
2790 pDisk->pBase = pImage->pNext;
2791
2792 if (pImage->pNext)
2793 pImage->pNext->pPrev = pImage->pPrev;
2794 else
2795 pDisk->pLast = pImage->pPrev;
2796
2797 pImage->pPrev = NULL;
2798 pImage->pNext = NULL;
2799
2800 pDisk->cImages--;
2801}
2802
2803/**
2804 * Allocates and initializes VDI HDD container.
2805 *
2806 * @returns Pointer to newly created HDD container with no one opened image file.
2807 * @returns NULL on failure, typically out of memory.
2808 */
2809VBOXDDU_DECL(PVDIDISK) VDIDiskCreate(void)
2810{
2811 PVDIDISK pDisk = (PVDIDISK)RTMemAllocZ(sizeof(VDIDISK));
2812 if (pDisk)
2813 vdiInitVDIDisk(pDisk);
2814 LogFlow(("VDIDiskCreate: returns pDisk=%X\n", pDisk));
2815 return pDisk;
2816}
2817
2818/**
2819 * Destroys VDI HDD container. If container has opened image files they will be closed.
2820 *
2821 * @param pDisk Pointer to VDI HDD container.
2822 */
2823VBOXDDU_DECL(void) VDIDiskDestroy(PVDIDISK pDisk)
2824{
2825 LogFlow(("VDIDiskDestroy: pDisk=%X\n", pDisk));
2826 /* sanity check */
2827 Assert(pDisk);
2828 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2829
2830 if (pDisk)
2831 {
2832 VDIDiskCloseAllImages(pDisk);
2833 RTMemFree(pDisk);
2834 }
2835}
2836
2837/**
2838 * Get working buffer size of VDI HDD container.
2839 *
2840 * @returns Working buffer size in bytes.
2841 */
2842VBOXDDU_DECL(unsigned) VDIDiskGetBufferSize(PVDIDISK pDisk)
2843{
2844 /* sanity check */
2845 Assert(pDisk);
2846 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2847
2848 LogFlow(("VDIDiskGetBufferSize: returns %u\n", pDisk->cbBuf));
2849 return pDisk->cbBuf;
2850}
2851
2852/**
2853 * Get read/write mode of VDI HDD.
2854 *
2855 * @returns Disk ReadOnly status.
2856 * @returns true if no one VDI image is opened in HDD container.
2857 */
2858VBOXDDU_DECL(bool) VDIDiskIsReadOnly(PVDIDISK pDisk)
2859{
2860 /* sanity check */
2861 Assert(pDisk);
2862 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2863
2864 if (pDisk->pLast)
2865 {
2866 LogFlow(("VDIDiskIsReadOnly: returns %u\n", pDisk->pLast->fReadOnly));
2867 return pDisk->pLast->fReadOnly;
2868 }
2869
2870 AssertMsgFailed(("No disk image is opened!\n"));
2871 return true;
2872}
2873
2874/**
2875 * Get disk size of VDI HDD container.
2876 *
2877 * @returns Virtual disk size in bytes.
2878 * @returns 0 if no one VDI image is opened in HDD container.
2879 */
2880VBOXDDU_DECL(uint64_t) VDIDiskGetSize(PVDIDISK pDisk)
2881{
2882 /* sanity check */
2883 Assert(pDisk);
2884 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2885
2886 if (pDisk->pBase)
2887 {
2888 LogFlow(("VDIDiskGetSize: returns %llu\n", getImageDiskSize(&pDisk->pBase->Header)));
2889 return getImageDiskSize(&pDisk->pBase->Header);
2890 }
2891
2892 AssertMsgFailed(("No disk image is opened!\n"));
2893 return 0;
2894}
2895
2896/**
2897 * Get block size of VDI HDD container.
2898 *
2899 * @returns VDI image block size in bytes.
2900 * @returns 0 if no one VDI image is opened in HDD container.
2901 */
2902VBOXDDU_DECL(unsigned) VDIDiskGetBlockSize(PVDIDISK pDisk)
2903{
2904 /* sanity check */
2905 Assert(pDisk);
2906 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2907
2908 if (pDisk->pBase)
2909 {
2910 LogFlow(("VDIDiskGetBlockSize: returns %u\n", getImageBlockSize(&pDisk->pBase->Header)));
2911 return getImageBlockSize(&pDisk->pBase->Header);
2912 }
2913
2914 AssertMsgFailed(("No disk image is opened!\n"));
2915 return 0;
2916}
2917
2918/**
2919 * Get virtual disk LCHS geometry stored in image file.
2920 *
2921 * @returns VBox status code.
2922 * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
2923 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry has been setted.
2924 * @param pDisk Pointer to VDI HDD container.
2925 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
2926 */
2927VBOXDDU_DECL(int) VDIDiskGetLCHSGeometry(PVDIDISK pDisk, PPDMMEDIAGEOMETRY pLCHSGeometry)
2928{
2929 /* sanity check */
2930 Assert(pDisk);
2931 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2932
2933 if (pDisk->pBase)
2934 {
2935 int rc = VINF_SUCCESS;
2936 VDIDISKGEOMETRY DummyGeo = { 0, 0, 0, VDI_GEOMETRY_SECTOR_SIZE };
2937 PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pDisk->pBase->Header);
2938 if (!pGeometry)
2939 pGeometry = &DummyGeo;
2940
2941 LogFlow(("%s: C/H/S = %u/%u/%u\n",
2942 __FUNCTION__, pGeometry->cCylinders, pGeometry->cHeads, pGeometry->cSectors));
2943 if ( pGeometry->cCylinders > 0
2944 && pGeometry->cHeads > 0
2945 && pGeometry->cSectors > 0)
2946 {
2947 pLCHSGeometry->cCylinders = pGeometry->cCylinders;
2948 pLCHSGeometry->cHeads = pGeometry->cHeads;
2949 pLCHSGeometry->cSectors = pGeometry->cSectors;
2950 }
2951 else
2952 rc = VERR_VDI_GEOMETRY_NOT_SET;
2953
2954 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
2955 return rc;
2956 }
2957
2958 AssertMsgFailed(("No disk image is opened!\n"));
2959 return VERR_VDI_NOT_OPENED;
2960}
2961
2962/**
2963 * Store virtual disk LCHS geometry into base image file of HDD container.
2964 *
2965 * Note that in case of unrecoverable error all images of HDD container will be closed.
2966 *
2967 * @returns VBox status code.
2968 * @returns VERR_VDI_NOT_OPENED if no one VDI image is opened in HDD container.
2969 * @param pDisk Pointer to VDI HDD container.
2970 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
2971 */
2972VBOXDDU_DECL(int) VDIDiskSetLCHSGeometry(PVDIDISK pDisk, PCPDMMEDIAGEOMETRY pLCHSGeometry)
2973{
2974 LogFlow(("%s: C/H/S = %u/%u/%u\n", __FUNCTION__, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2975 /* sanity check */
2976 Assert(pDisk);
2977 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2978
2979 if (pDisk->pBase)
2980 {
2981 int rc = VINF_SUCCESS;
2982 PVDIDISKGEOMETRY pGeometry = getImageLCHSGeometry(&pDisk->pBase->Header);
2983 if (pGeometry)
2984 {
2985 pGeometry->cCylinders = pLCHSGeometry->cCylinders;
2986 pGeometry->cHeads = pLCHSGeometry->cHeads;
2987 pGeometry->cSectors = pLCHSGeometry->cSectors;
2988 pGeometry->cbSector = VDI_GEOMETRY_SECTOR_SIZE;
2989
2990 /* Update header information in base image file. */
2991 rc = vdiUpdateReadOnlyHeader(pDisk->pBase);
2992 }
2993 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
2994 return rc;
2995 }
2996
2997 AssertMsgFailed(("No disk image is opened!\n"));
2998 return VERR_VDI_NOT_OPENED;
2999}
3000
3001/**
3002 * Get number of opened images in HDD container.
3003 *
3004 * @returns Number of opened images for HDD container. 0 if no images is opened.
3005 * @param pDisk Pointer to VDI HDD container.
3006 */
3007VBOXDDU_DECL(int) VDIDiskGetImagesCount(PVDIDISK pDisk)
3008{
3009 /* sanity check */
3010 Assert(pDisk);
3011 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3012
3013 LogFlow(("VDIDiskGetImagesCount: returns %d\n", pDisk->cImages));
3014 return pDisk->cImages;
3015}
3016
3017static PVDIIMAGEDESC vdiGetImageByNumber(PVDIDISK pDisk, int nImage)
3018{
3019 PVDIIMAGEDESC pImage = pDisk->pBase;
3020 while (pImage && nImage)
3021 {
3022 pImage = pImage->pNext;
3023 nImage--;
3024 }
3025 return pImage;
3026}
3027
3028/**
3029 * Get version of opened image of HDD container.
3030 *
3031 * @returns VBox status code.
3032 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3033 * @param pDisk Pointer to VDI HDD container.
3034 * @param nImage Image number, counts from 0. 0 is always base image of container.
3035 * @param puVersion Where to store the image version.
3036 */
3037VBOXDDU_DECL(int) VDIDiskGetImageVersion(PVDIDISK pDisk, int nImage, unsigned *puVersion)
3038{
3039 /* sanity check */
3040 Assert(pDisk);
3041 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3042 Assert(puVersion);
3043
3044 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3045 Assert(pImage);
3046
3047 if (pImage)
3048 {
3049 *puVersion = pImage->PreHeader.u32Version;
3050 LogFlow(("VDIDiskGetImageVersion: returns %08X\n", pImage->PreHeader.u32Version));
3051 return VINF_SUCCESS;
3052 }
3053
3054 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3055 return VERR_VDI_IMAGE_NOT_FOUND;
3056}
3057
3058/**
3059 * Get filename of opened image of HDD container.
3060 *
3061 * @returns VBox status code.
3062 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3063 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
3064 * @param pDisk Pointer to VDI HDD container.
3065 * @param nImage Image number, counts from 0. 0 is always base image of container.
3066 * @param pszFilename Where to store the image file name.
3067 * @param cbFilename Size of buffer pszFilename points to.
3068 */
3069VBOXDDU_DECL(int) VDIDiskGetImageFilename(PVDIDISK pDisk, int nImage, char *pszFilename, unsigned cbFilename)
3070{
3071 /* sanity check */
3072 Assert(pDisk);
3073 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3074 Assert(pszFilename);
3075
3076 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3077 Assert(pImage);
3078
3079 if (pImage)
3080 {
3081 size_t cb = strlen(pImage->szFilename);
3082 if (cb < cbFilename)
3083 {
3084 /* memcpy is much better than strncpy. */
3085 memcpy(pszFilename, pImage->szFilename, cb + 1);
3086 LogFlow(("VDIDiskGetImageFilename: returns VINF_SUCCESS, filename=\"%s\" nImage=%d\n",
3087 pszFilename, nImage));
3088 return VINF_SUCCESS;
3089 }
3090 else
3091 {
3092 AssertMsgFailed(("Out of buffer space, cbFilename=%d needed=%d\n", cbFilename, cb + 1));
3093 return VERR_BUFFER_OVERFLOW;
3094 }
3095 }
3096
3097 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3098 return VERR_VDI_IMAGE_NOT_FOUND;
3099}
3100
3101/**
3102 * Get the comment line of opened image of HDD container.
3103 *
3104 * @returns VBox status code.
3105 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3106 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
3107 * @param pDisk Pointer to VDI HDD container.
3108 * @param nImage Image number, counts from 0. 0 is always base image of container.
3109 * @param pszComment Where to store the comment string of image. NULL is ok.
3110 * @param cbComment The size of pszComment buffer. 0 is ok.
3111 */
3112VBOXDDU_DECL(int) VDIDiskGetImageComment(PVDIDISK pDisk, int nImage, char *pszComment, unsigned cbComment)
3113{
3114 /* sanity check */
3115 Assert(pDisk);
3116 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3117 Assert(pszComment);
3118
3119 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3120 Assert(pImage);
3121
3122 if (pImage)
3123 {
3124 char *pszTmp = getImageComment(&pImage->Header);
3125 size_t cb = strlen(pszTmp);
3126 if (cb < cbComment)
3127 {
3128 /* memcpy is much better than strncpy. */
3129 memcpy(pszComment, pszTmp, cb + 1);
3130 LogFlow(("VDIDiskGetImageComment: returns VINF_SUCCESS, comment=\"%s\" nImage=%d\n",
3131 pszTmp, nImage));
3132 return VINF_SUCCESS;
3133 }
3134 else
3135 {
3136 AssertMsgFailed(("Out of buffer space, cbComment=%d needed=%d\n", cbComment, cb + 1));
3137 return VERR_BUFFER_OVERFLOW;
3138 }
3139 }
3140
3141 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3142 return VERR_VDI_IMAGE_NOT_FOUND;
3143}
3144
3145/**
3146 * Get type of opened image of HDD container.
3147 *
3148 * @returns VBox status code.
3149 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3150 * @param pDisk Pointer to VDI HDD container.
3151 * @param nImage Image number, counts from 0. 0 is always base image of container.
3152 * @param penmType Where to store the image type.
3153 */
3154VBOXDDU_DECL(int) VDIDiskGetImageType(PVDIDISK pDisk, int nImage, PVDIIMAGETYPE penmType)
3155{
3156 /* sanity check */
3157 Assert(pDisk);
3158 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3159 Assert(penmType);
3160
3161 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3162 Assert(pImage);
3163
3164 if (pImage)
3165 {
3166 *penmType = getImageType(&pImage->Header);
3167 LogFlow(("VDIDiskGetImageType: returns VINF_SUCCESS, type=%X nImage=%d\n",
3168 *penmType, nImage));
3169 return VINF_SUCCESS;
3170 }
3171
3172 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3173 return VERR_VDI_IMAGE_NOT_FOUND;
3174}
3175
3176/**
3177 * Get flags of opened image of HDD container.
3178 *
3179 * @returns VBox status code.
3180 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3181 * @param pDisk Pointer to VDI HDD container.
3182 * @param nImage Image number, counts from 0. 0 is always base image of container.
3183 * @param pfFlags Where to store the image flags.
3184 */
3185VBOXDDU_DECL(int) VDIDiskGetImageFlags(PVDIDISK pDisk, int nImage, unsigned *pfFlags)
3186{
3187 /* sanity check */
3188 Assert(pDisk);
3189 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3190 Assert(pfFlags);
3191
3192 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3193 Assert(pImage);
3194
3195 if (pImage)
3196 {
3197 *pfFlags = getImageFlags(&pImage->Header);
3198 LogFlow(("VDIDiskGetImageFlags: returns VINF_SUCCESS, flags=%08X nImage=%d\n",
3199 *pfFlags, nImage));
3200 return VINF_SUCCESS;
3201 }
3202
3203 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3204 return VERR_VDI_IMAGE_NOT_FOUND;
3205}
3206
3207/**
3208 * Get Uuid of opened image of HDD container.
3209 *
3210 * @returns VBox status code.
3211 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3212 * @param pDisk Pointer to VDI HDD container.
3213 * @param nImage Image number, counts from 0. 0 is always base image of container.
3214 * @param pUuid Where to store the image creation uuid.
3215 */
3216VBOXDDU_DECL(int) VDIDiskGetImageUuid(PVDIDISK pDisk, int nImage, PRTUUID pUuid)
3217{
3218 /* sanity check */
3219 Assert(pDisk);
3220 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3221 Assert(pUuid);
3222
3223 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3224 Assert(pImage);
3225
3226 if (pImage)
3227 {
3228 *pUuid = *getImageCreationUUID(&pImage->Header);
3229 LogFlow(("VDIDiskGetImageUuid: returns VINF_SUCCESS, uuid={%Vuuid} nImage=%d\n",
3230 pUuid, nImage));
3231 return VINF_SUCCESS;
3232 }
3233
3234 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3235 return VERR_VDI_IMAGE_NOT_FOUND;
3236}
3237
3238/**
3239 * Get last modification Uuid of opened image of HDD container.
3240 *
3241 * @returns VBox status code.
3242 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3243 * @param pDisk Pointer to VDI HDD container.
3244 * @param nImage Image number, counts from 0. 0 is always base image of container.
3245 * @param pUuid Where to store the image modification uuid.
3246 */
3247VBOXDDU_DECL(int) VDIDiskGetImageModificationUuid(PVDIDISK pDisk, int nImage, PRTUUID pUuid)
3248{
3249 /* sanity check */
3250 Assert(pDisk);
3251 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3252 Assert(pUuid);
3253
3254 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3255 Assert(pImage);
3256
3257 if (pImage)
3258 {
3259 *pUuid = *getImageModificationUUID(&pImage->Header);
3260 LogFlow(("VDIDiskGetImageModificationUuid: returns VINF_SUCCESS, uuid={%Vuuid} nImage=%d\n",
3261 pUuid, nImage));
3262 return VINF_SUCCESS;
3263 }
3264
3265 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3266 return VERR_VDI_IMAGE_NOT_FOUND;
3267}
3268
3269/**
3270 * Get Uuid of opened image's parent image.
3271 *
3272 * @returns VBox status code.
3273 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
3274 * @param pDisk Pointer to VDI HDD container.
3275 * @param nImage Image number, counts from 0. 0 is always base image of the container.
3276 * @param pUuid Where to store the image creation uuid.
3277 */
3278VBOXDDU_DECL(int) VDIDiskGetParentImageUuid(PVDIDISK pDisk, int nImage, PRTUUID pUuid)
3279{
3280 /* sanity check */
3281 Assert(pDisk);
3282 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3283 Assert(pUuid);
3284
3285 PVDIIMAGEDESC pImage = vdiGetImageByNumber(pDisk, nImage);
3286 if (pImage)
3287 {
3288 *pUuid = *getImageParentUUID(&pImage->Header);
3289 LogFlow(("VDIDiskGetParentImageUuid: returns VINF_SUCCESS, *pUuid={%Vuuid} nImage=%d\n",
3290 pUuid, nImage));
3291 return VINF_SUCCESS;
3292 }
3293
3294 AssertMsgFailed(("Image %d was not found (cImages=%d)\n", nImage, pDisk->cImages));
3295 return VERR_VDI_IMAGE_NOT_FOUND;
3296}
3297
3298/**
3299 * Relock the image as read/write or read-only.
3300 */
3301int vdiChangeImageMode(PVDIIMAGEDESC pImage, bool fReadOnly)
3302{
3303 Assert(pImage);
3304
3305 if ( !fReadOnly
3306 && pImage->fOpen & VDI_OPEN_FLAGS_READONLY)
3307 {
3308 /* Can't switch read-only opened image to read-write mode. */
3309 Log(("vdiChangeImageMode: can't switch r/o image to r/w mode, filename=\"%s\" fOpen=%X\n",
3310 pImage->szFilename, pImage->fOpen));
3311 return VERR_VDI_IMAGE_READ_ONLY;
3312 }
3313
3314 /* Flush last image changes if was r/w mode. */
3315 VDIFlushImage(pImage);
3316
3317 /* Change image locking. */
3318 uint64_t cbLock = pImage->offStartData
3319 + ((uint64_t)getImageBlocks(&pImage->Header) << pImage->uShiftIndex2Offset);
3320 int rc = RTFileChangeLock(pImage->File,
3321 (fReadOnly) ?
3322 RTFILE_LOCK_READ | RTFILE_LOCK_IMMEDIATELY :
3323 RTFILE_LOCK_WRITE | RTFILE_LOCK_IMMEDIATELY,
3324 0,
3325 cbLock);
3326 if (VBOX_SUCCESS(rc))
3327 {
3328 pImage->fReadOnly = fReadOnly;
3329 Log(("vdiChangeImageMode: Image \"%s\" mode changed to %s\n",
3330 pImage->szFilename, (pImage->fReadOnly) ? "read-only" : "read/write"));
3331 return VINF_SUCCESS;
3332 }
3333
3334 /* Check for the most bad error in the world. Damn! It must never happens in real life! */
3335 if (rc == VERR_FILE_LOCK_LOST)
3336 {
3337 /* And what we can do now?! */
3338 AssertMsgFailed(("Image lock has been lost for file=\"%s\"", pImage->szFilename));
3339 Log(("vdiChangeImageMode: image lock has been lost for file=\"%s\", blocking on r/o lock wait",
3340 pImage->szFilename));
3341
3342 /* Try to obtain read lock in blocking mode. Maybe it's a very bad method. */
3343 rc = RTFileLock(pImage->File, RTFILE_LOCK_READ | RTFILE_LOCK_WAIT, 0, cbLock);
3344 AssertReleaseRC(rc);
3345
3346 pImage->fReadOnly = false;
3347 if (pImage->fReadOnly != fReadOnly)
3348 rc = VERR_FILE_LOCK_VIOLATION;
3349 }
3350
3351 Log(("vdiChangeImageMode: Image \"%s\" mode change failed with rc=%Vrc, mode is %s\n",
3352 pImage->szFilename, rc, (pImage->fReadOnly) ? "read-only" : "read/write"));
3353
3354 return rc;
3355}
3356
3357/**
3358 * internal: try to save header in image file even if image is in read-only mode.
3359 */
3360static int vdiUpdateReadOnlyHeader(PVDIIMAGEDESC pImage)
3361{
3362 int rc = VINF_SUCCESS;
3363
3364 if (pImage->fReadOnly)
3365 {
3366 rc = vdiChangeImageMode(pImage, false);
3367 if (VBOX_SUCCESS(rc))
3368 {
3369 VDIFlushImage(pImage);
3370 rc = vdiChangeImageMode(pImage, true);
3371 AssertReleaseRC(rc);
3372 }
3373 }
3374 else
3375 VDIFlushImage(pImage);
3376
3377 return rc;
3378}
3379
3380/**
3381 * Opens an image file.
3382 *
3383 * The first opened image file in a HDD container must have a base image type,
3384 * others (next opened images) must be a differencing or undo images.
3385 * Linkage is checked for differencing image to be in consistence with the previously opened image.
3386 * When a next differencing image is opened and the last image was opened in read/write access
3387 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
3388 * other processes to use images in read-only mode too.
3389 *
3390 * Note that the image can be opened in read-only mode if a read/write open is not possible.
3391 * Use VDIDiskIsReadOnly to check open mode.
3392 *
3393 * @returns VBox status code.
3394 * @param pDisk Pointer to VDI HDD container.
3395 * @param pszFilename Name of the image file to open.
3396 * @param fOpen Image file open mode, see VDI_OPEN_FLAGS_* constants.
3397 */
3398VBOXDDU_DECL(int) VDIDiskOpenImage(PVDIDISK pDisk, const char *pszFilename, unsigned fOpen)
3399{
3400 /* sanity check */
3401 Assert(pDisk);
3402 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3403
3404 /* Check arguments. */
3405 if ( !pszFilename
3406 || *pszFilename == '\0'
3407 || (fOpen & ~VDI_OPEN_FLAGS_MASK))
3408 {
3409 AssertMsgFailed(("Invalid arguments: pszFilename=%p fOpen=%x\n", pszFilename, fOpen));
3410 return VERR_INVALID_PARAMETER;
3411 }
3412 LogFlow(("VDIDiskOpenImage: pszFilename=\"%s\" fOpen=%X\n", pszFilename, fOpen));
3413
3414 PVDIIMAGEDESC pImage;
3415 int rc = vdiOpenImage(&pImage, pszFilename, fOpen, pDisk->pLast);
3416 if (VBOX_SUCCESS(rc))
3417 {
3418 if (pDisk->pLast)
3419 {
3420 /* Opening differencing image. */
3421 if (!pDisk->pLast->fReadOnly)
3422 {
3423 /*
3424 * Previous image is opened in read/write mode -> switch it into read-only.
3425 */
3426 rc = vdiChangeImageMode(pDisk->pLast, true);
3427 }
3428 }
3429 else
3430 {
3431 /* Opening base image, check its type. */
3432 if ( getImageType(&pImage->Header) != VDI_IMAGE_TYPE_NORMAL
3433 && getImageType(&pImage->Header) != VDI_IMAGE_TYPE_FIXED)
3434 {
3435 rc = VERR_VDI_INVALID_TYPE;
3436 }
3437 }
3438
3439 if (VBOX_SUCCESS(rc))
3440 vdiAddImageToList(pDisk, pImage);
3441 else
3442 vdiCloseImage(pImage);
3443 }
3444
3445 LogFlow(("VDIDiskOpenImage: returns %Vrc\n", rc));
3446 return rc;
3447}
3448
3449/**
3450 * Closes the last opened image file in the HDD container. Leaves all changes inside it.
3451 * If previous image file was opened in read-only mode (that is normal) and closing image
3452 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
3453 * will be reopened in read/write mode.
3454 *
3455 * @param pDisk Pointer to VDI HDD container.
3456 */
3457VBOXDDU_DECL(void) VDIDiskCloseImage(PVDIDISK pDisk)
3458{
3459 /* sanity check */
3460 Assert(pDisk);
3461 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3462
3463 PVDIIMAGEDESC pImage = pDisk->pLast;
3464 if (pImage)
3465 {
3466 LogFlow(("VDIDiskCloseImage: closing image \"%s\"\n", pImage->szFilename));
3467
3468 bool fWasReadOnly = pImage->fReadOnly;
3469 vdiRemoveImageFromList(pDisk, pImage);
3470 vdiCloseImage(pImage);
3471
3472 if ( !fWasReadOnly
3473 && pDisk->pLast
3474 && pDisk->pLast->fReadOnly
3475 && !(pDisk->pLast->fOpen & VDI_OPEN_FLAGS_READONLY))
3476 {
3477 /*
3478 * Closed image was opened in read/write mode, previous image was opened
3479 * in read-only mode, try to switch it into read/write.
3480 */
3481 int rc = vdiChangeImageMode(pDisk->pLast, false);
3482 NOREF(rc); /* gcc still hates unused variables... */
3483 }
3484
3485 return;
3486 }
3487 AssertMsgFailed(("No images to close\n"));
3488}
3489
3490/**
3491 * Closes all opened image files in HDD container.
3492 *
3493 * @param pDisk Pointer to VDI HDD container.
3494 */
3495VBOXDDU_DECL(void) VDIDiskCloseAllImages(PVDIDISK pDisk)
3496{
3497 LogFlow(("VDIDiskCloseAllImages:\n"));
3498 /* sanity check */
3499 Assert(pDisk);
3500 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3501
3502 PVDIIMAGEDESC pImage = pDisk->pLast;
3503 while (pImage)
3504 {
3505 PVDIIMAGEDESC pPrev = pImage->pPrev;
3506 vdiRemoveImageFromList(pDisk, pImage);
3507 vdiCloseImage(pImage);
3508 pImage = pPrev;
3509 }
3510 Assert(pDisk->pLast == NULL);
3511}
3512
3513/**
3514 * Commits last opened differencing/undo image file of HDD container to previous one.
3515 * If previous image file was opened in read-only mode (that must be always so) it is reopened
3516 * as read/write to do commit operation.
3517 * After successfull commit the previous image file again reopened in read-only mode, last opened
3518 * image file is cleared of data and remains open and active in HDD container.
3519 * If you want to delete image after commit you must do it manually by VDIDiskCloseImage and
3520 * VDIDeleteImage calls.
3521 *
3522 * Note that in case of unrecoverable error all images of HDD container will be closed.
3523 *
3524 * @returns VBox status code.
3525 * @param pDisk Pointer to VDI HDD container.
3526 * @param pfnProgress Progress callback. Optional.
3527 * @param pvUser User argument for the progress callback.
3528 * @remark Only used by tstVDI.
3529 */
3530VBOXDDU_DECL(int) VDIDiskCommitLastDiff(PVDIDISK pDisk, PFNVMPROGRESS pfnProgress, void *pvUser)
3531{
3532 LogFlow(("VDIDiskCommitLastDiff:\n"));
3533 /* sanity check */
3534 Assert(pDisk);
3535 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3536
3537 int rc = VINF_SUCCESS;
3538 PVDIIMAGEDESC pImage = pDisk->pLast;
3539 if (!pImage)
3540 {
3541 AssertMsgFailed(("No disk image is opened!\n"));
3542 return VERR_VDI_NOT_OPENED;
3543 }
3544
3545 if (pImage->fReadOnly)
3546 {
3547 AssertMsgFailed(("Image \"%s\" is read-only!\n", pImage->szFilename));
3548 return VERR_VDI_IMAGE_READ_ONLY;
3549 }
3550
3551 if (!pImage->pPrev)
3552 {
3553 AssertMsgFailed(("No images to commit to!\n"));
3554 return VERR_VDI_NO_DIFF_IMAGES;
3555 }
3556
3557 bool fWasReadOnly = pImage->pPrev->fReadOnly;
3558 if (fWasReadOnly)
3559 {
3560 /* Change previous image mode to r/w. */
3561 rc = vdiChangeImageMode(pImage->pPrev, false);
3562 if (VBOX_FAILURE(rc))
3563 {
3564 Log(("VDIDiskCommitLastDiff: can't switch previous image into r/w mode, rc=%Vrc\n", rc));
3565 return rc;
3566 }
3567 }
3568
3569 rc = vdiCommitToImage(pDisk, pImage->pPrev, pfnProgress, pvUser);
3570 if (VBOX_SUCCESS(rc) && fWasReadOnly)
3571 {
3572 /* Change previous image mode back to r/o. */
3573 rc = vdiChangeImageMode(pImage->pPrev, true);
3574 }
3575
3576 if (VBOX_FAILURE(rc))
3577 {
3578 /* Failed! Close all images, can't work with VHDD at all. */
3579 VDIDiskCloseAllImages(pDisk);
3580 AssertMsgFailed(("Fatal: commit failed, rc=%Vrc\n", rc));
3581 }
3582
3583 return rc;
3584}
3585
3586/**
3587 * Creates and opens a new differencing image file in HDD container.
3588 * See comments for VDIDiskOpenImage function about differencing images.
3589 *
3590 * @returns VBox status code.
3591 * @param pDisk Pointer to VDI HDD container.
3592 * @param pszFilename Name of the image file to create and open.
3593 * @param pszComment Pointer to image comment. NULL is ok.
3594 * @param pfnProgress Progress callback. Optional.
3595 * @param pvUser User argument for the progress callback.
3596 * @remark Only used by tstVDI.
3597 */
3598VBOXDDU_DECL(int) VDIDiskCreateOpenDifferenceImage(PVDIDISK pDisk, const char *pszFilename,
3599 const char *pszComment, PFNVMPROGRESS pfnProgress,
3600 void *pvUser)
3601{
3602 LogFlow(("VDIDiskCreateOpenDifferenceImage:\n"));
3603 /* sanity check */
3604 Assert(pDisk);
3605 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3606 Assert(pszFilename);
3607
3608 if (!pDisk->pLast)
3609 {
3610 AssertMsgFailed(("No disk image is opened!\n"));
3611 return VERR_VDI_NOT_OPENED;
3612 }
3613
3614 /* Flush last parent image changes if possible. */
3615 VDIFlushImage(pDisk->pLast);
3616
3617 int rc = vdiCreateImage(pszFilename,
3618 VDI_IMAGE_TYPE_DIFF,
3619 VDI_IMAGE_FLAGS_DEFAULT,
3620 getImageDiskSize(&pDisk->pLast->Header),
3621 pszComment,
3622 pDisk->pLast,
3623 pfnProgress, pvUser);
3624 if (VBOX_SUCCESS(rc))
3625 {
3626 rc = VDIDiskOpenImage(pDisk, pszFilename, VDI_OPEN_FLAGS_NORMAL);
3627 if (VBOX_FAILURE(rc))
3628 VDIDeleteImage(pszFilename);
3629 }
3630 LogFlow(("VDIDiskCreateOpenDifferenceImage: returns %Vrc, filename=\"%s\"\n", rc, pszFilename));
3631 return rc;
3632}
3633
3634/**
3635 * internal: debug image dump.
3636 *
3637 * @remark Only used by tstVDI.
3638 */
3639static void vdiDumpImage(PVDIIMAGEDESC pImage)
3640{
3641 RTLogPrintf("Dumping VDI image \"%s\" mode=%s fOpen=%X File=%08X\n",
3642 pImage->szFilename,
3643 (pImage->fReadOnly) ? "r/o" : "r/w",
3644 pImage->fOpen,
3645 pImage->File);
3646 RTLogPrintf("Header: Version=%08X Type=%X Flags=%X Size=%llu\n",
3647 pImage->PreHeader.u32Version,
3648 getImageType(&pImage->Header),
3649 getImageFlags(&pImage->Header),
3650 getImageDiskSize(&pImage->Header));
3651 RTLogPrintf("Header: cbBlock=%u cbBlockExtra=%u cBlocks=%u cBlocksAllocated=%u\n",
3652 getImageBlockSize(&pImage->Header),
3653 getImageExtraBlockSize(&pImage->Header),
3654 getImageBlocks(&pImage->Header),
3655 getImageBlocksAllocated(&pImage->Header));
3656 RTLogPrintf("Header: offBlocks=%u offData=%u\n",
3657 getImageBlocksOffset(&pImage->Header),
3658 getImageDataOffset(&pImage->Header));
3659 PVDIDISKGEOMETRY pg = getImageLCHSGeometry(&pImage->Header);
3660 if (pg)
3661 RTLogPrintf("Header: Geometry: C/H/S=%u/%u/%u cbSector=%u\n",
3662 pg->cCylinders, pg->cHeads, pg->cSectors, pg->cbSector);
3663 RTLogPrintf("Header: uuidCreation={%Vuuid}\n", getImageCreationUUID(&pImage->Header));
3664 RTLogPrintf("Header: uuidModification={%Vuuid}\n", getImageModificationUUID(&pImage->Header));
3665 RTLogPrintf("Header: uuidParent={%Vuuid}\n", getImageParentUUID(&pImage->Header));
3666 if (GET_MAJOR_HEADER_VERSION(&pImage->Header) >= 1)
3667 RTLogPrintf("Header: uuidParentModification={%Vuuid}\n", getImageParentModificationUUID(&pImage->Header));
3668 RTLogPrintf("Image: fFlags=%08X offStartBlocks=%u offStartData=%u\n",
3669 pImage->fFlags, pImage->offStartBlocks, pImage->offStartData);
3670 RTLogPrintf("Image: uBlockMask=%08X uShiftIndex2Offset=%u uShiftOffset2Index=%u offStartBlockData=%u\n",
3671 pImage->uBlockMask,
3672 pImage->uShiftIndex2Offset,
3673 pImage->uShiftOffset2Index,
3674 pImage->offStartBlockData);
3675
3676 unsigned uBlock, cBlocksNotFree, cBadBlocks, cBlocks = getImageBlocks(&pImage->Header);
3677 for (uBlock=0, cBlocksNotFree=0, cBadBlocks=0; uBlock<cBlocks; uBlock++)
3678 {
3679 if (IS_VDI_IMAGE_BLOCK_ALLOCATED(pImage->paBlocks[uBlock]))
3680 {
3681 cBlocksNotFree++;
3682 if (pImage->paBlocks[uBlock] >= cBlocks)
3683 cBadBlocks++;
3684 }
3685 }
3686 if (cBlocksNotFree != getImageBlocksAllocated(&pImage->Header))
3687 {
3688 RTLogPrintf("!! WARNING: %u blocks actually allocated (cBlocksAllocated=%u) !!\n",
3689 cBlocksNotFree, getImageBlocksAllocated(&pImage->Header));
3690 }
3691 if (cBadBlocks)
3692 {
3693 RTLogPrintf("!! WARNING: %u bad blocks found !!\n",
3694 cBadBlocks);
3695 }
3696}
3697
3698/**
3699 * Debug helper - dumps all opened images of HDD container into the log file.
3700 *
3701 * @param pDisk Pointer to VDI HDD container.
3702 * @remark Only used by tstVDI and vditool
3703 */
3704VBOXDDU_DECL(void) VDIDiskDumpImages(PVDIDISK pDisk)
3705{
3706 /* sanity check */
3707 Assert(pDisk);
3708 AssertMsg(pDisk->u32Signature == VDIDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
3709
3710 RTLogPrintf("--- Dumping VDI Disk, Images=%u\n", pDisk->cImages);
3711 for (PVDIIMAGEDESC pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
3712 vdiDumpImage(pImage);
3713}
3714
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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