VirtualBox

source: vbox/trunk/src/VBox/Storage/Parallels.cpp@ 38413

Last change on this file since 38413 was 38413, checked in by vboxsync, 14 years ago

VD/Parallels: Cleanup, fixes and implement support for parallels image creation to make I/O testing possible

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 53.0 KB
Line 
1/* $Id: Parallels.cpp 38413 2011-08-11 11:18:44Z vboxsync $ */
2/** @file
3 *
4 * Parallels hdd disk image, core code.
5 */
6
7/*
8 * Copyright (C) 2006-2010 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.215389.xyz. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#define LOG_GROUP LOG_GROUP_VD_PARALLELS
20#include <VBox/vd-plugin.h>
21#include <VBox/err.h>
22
23#include <VBox/log.h>
24#include <iprt/assert.h>
25#include <iprt/mem.h>
26#include <iprt/uuid.h>
27#include <iprt/path.h>
28#include <iprt/string.h>
29
30#define PARALLELS_HEADER_MAGIC "WithoutFreeSpace"
31#define PARALLELS_DISK_VERSION 2
32
33/** The header of the parallels disk. */
34#pragma pack(1)
35typedef struct ParallelsHeader
36{
37 /** The magic header to identify a parallels hdd image. */
38 char HeaderIdentifier[16];
39 /** The version of the disk image. */
40 uint32_t uVersion;
41 /** The number of heads the hdd has. */
42 uint32_t cHeads;
43 /** Number of cylinders. */
44 uint32_t cCylinders;
45 /** Number of sectors per track. */
46 uint32_t cSectorsPerTrack;
47 /** Number of entries in the allocation bitmap. */
48 uint32_t cEntriesInAllocationBitmap;
49 /** Total number of sectors. */
50 uint32_t cSectors;
51 /** Padding. */
52 char Padding[24];
53} ParallelsHeader;
54#pragma pack()
55
56/**
57 * Parallels image structure.
58 */
59typedef struct PARALLELSIMAGE
60{
61 /** Image file name. */
62 const char *pszFilename;
63 /** Opaque storage handle. */
64 PVDIOSTORAGE pStorage;
65
66 /** I/O interface. */
67 PVDINTERFACE pInterfaceIO;
68 /** I/O interface callbacks. */
69 PVDINTERFACEIOINT pInterfaceIOCallbacks;
70
71 /** Pointer to the per-disk VD interface list. */
72 PVDINTERFACE pVDIfsDisk;
73 /** Pointer to the per-image VD interface list. */
74 PVDINTERFACE pVDIfsImage;
75 /** Error interface. */
76 PVDINTERFACE pInterfaceError;
77 /** Error interface callbacks. */
78 PVDINTERFACEERROR pInterfaceErrorCallbacks;
79
80 /** Open flags passed by VBoxHDD layer. */
81 unsigned uOpenFlags;
82 /** Image flags defined during creation or determined during open. */
83 unsigned uImageFlags;
84 /** Total size of the image. */
85 uint64_t cbSize;
86
87 /** Physical geometry of this image. */
88 VDGEOMETRY PCHSGeometry;
89 /** Logical geometry of this image. */
90 VDGEOMETRY LCHSGeometry;
91
92 /** Pointer to the allocation bitmap. */
93 uint32_t *pAllocationBitmap;
94 /** Entries in the allocation bitmap. */
95 uint64_t cAllocationBitmapEntries;
96 /** Flag whether the allocation bitmap was changed. */
97 bool fAllocationBitmapChanged;
98 /** Current file size. */
99 uint64_t cbFileCurrent;
100} PARALLELSIMAGE, *PPARALLELSIMAGE;
101
102/*******************************************************************************
103* Static Variables *
104*******************************************************************************/
105
106/** NULL-terminated array of supported file extensions. */
107static const VDFILEEXTENSION s_aParallelsFileExtensions[] =
108{
109 {"hdd", VDTYPE_HDD},
110 {NULL, VDTYPE_INVALID}
111};
112
113/***************************************************
114 * Internal functions *
115 **************************************************/
116
117/**
118 * Internal: signal an error to the frontend.
119 */
120DECLINLINE(int) parallelsError(PPARALLELSIMAGE pImage, int rc, RT_SRC_POS_DECL,
121 const char *pszFormat, ...)
122{
123 va_list va;
124 va_start(va, pszFormat);
125 if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
126 pImage->pInterfaceErrorCallbacks->pfnError(pImage->pInterfaceError->pvUser, rc, RT_SRC_POS_ARGS,
127 pszFormat, va);
128 va_end(va);
129 return rc;
130}
131
132/**
133 * Internal: signal an informational message to the frontend.
134 */
135DECLINLINE(int) parallelsMessage(PPARALLELSIMAGE pImage, const char *pszFormat, ...)
136{
137 int rc = VINF_SUCCESS;
138 va_list va;
139 va_start(va, pszFormat);
140 if (pImage->pInterfaceError && pImage->pInterfaceErrorCallbacks)
141 rc = pImage->pInterfaceErrorCallbacks->pfnMessage(pImage->pInterfaceError->pvUser,
142 pszFormat, va);
143 va_end(va);
144 return rc;
145}
146
147
148DECLINLINE(int) parallelsFileOpen(PPARALLELSIMAGE pImage, const char *pszFilename,
149 uint32_t fOpen)
150{
151 return pImage->pInterfaceIOCallbacks->pfnOpen(pImage->pInterfaceIO->pvUser,
152 pszFilename, fOpen,
153 &pImage->pStorage);
154}
155
156DECLINLINE(int) parallelsFileClose(PPARALLELSIMAGE pImage)
157{
158 return pImage->pInterfaceIOCallbacks->pfnClose(pImage->pInterfaceIO->pvUser,
159 pImage->pStorage);
160}
161
162DECLINLINE(int) parallelsFileDelete(PPARALLELSIMAGE pImage, const char *pszFilename)
163{
164 return pImage->pInterfaceIOCallbacks->pfnDelete(pImage->pInterfaceIO->pvUser,
165 pszFilename);
166}
167
168DECLINLINE(int) parallelsFileMove(PPARALLELSIMAGE pImage, const char *pszSrc,
169 const char *pszDst, unsigned fMove)
170{
171 return pImage->pInterfaceIOCallbacks->pfnMove(pImage->pInterfaceIO->pvUser,
172 pszSrc, pszDst, fMove);
173}
174
175DECLINLINE(int) parallelsFileGetSize(PPARALLELSIMAGE pImage, uint64_t *pcbSize)
176{
177 return pImage->pInterfaceIOCallbacks->pfnGetSize(pImage->pInterfaceIO->pvUser,
178 pImage->pStorage, pcbSize);
179}
180
181DECLINLINE(int) parallelsFileSetSize(PPARALLELSIMAGE pImage, uint64_t cbSize)
182{
183 return pImage->pInterfaceIOCallbacks->pfnSetSize(pImage->pInterfaceIO->pvUser,
184 pImage->pStorage, cbSize);
185}
186
187DECLINLINE(int) parallelsFileWriteSync(PPARALLELSIMAGE pImage, uint64_t uOffset,
188 const void *pvBuffer, size_t cbBuffer,
189 size_t *pcbWritten)
190{
191 return pImage->pInterfaceIOCallbacks->pfnWriteSync(pImage->pInterfaceIO->pvUser,
192 pImage->pStorage, uOffset,
193 pvBuffer, cbBuffer, pcbWritten);
194}
195
196DECLINLINE(int) parallelsFileReadSync(PPARALLELSIMAGE pImage, uint64_t uOffset,
197 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
198{
199 return pImage->pInterfaceIOCallbacks->pfnReadSync(pImage->pInterfaceIO->pvUser,
200 pImage->pStorage, uOffset,
201 pvBuffer, cbBuffer, pcbRead);
202}
203
204DECLINLINE(int) parallelsFileFlushSync(PPARALLELSIMAGE pImage)
205{
206 return pImage->pInterfaceIOCallbacks->pfnFlushSync(pImage->pInterfaceIO->pvUser,
207 pImage->pStorage);
208}
209
210DECLINLINE(int) parallelsFileReadUserAsync(PPARALLELSIMAGE pImage, uint64_t uOffset,
211 PVDIOCTX pIoCtx, size_t cbRead)
212{
213 return pImage->pInterfaceIOCallbacks->pfnReadUserAsync(pImage->pInterfaceIO->pvUser,
214 pImage->pStorage,
215 uOffset, pIoCtx,
216 cbRead);
217}
218
219DECLINLINE(int) parallelsFileWriteUserAsync(PPARALLELSIMAGE pImage, uint64_t uOffset,
220 PVDIOCTX pIoCtx, size_t cbWrite,
221 PFNVDXFERCOMPLETED pfnComplete,
222 void *pvCompleteUser)
223{
224 return pImage->pInterfaceIOCallbacks->pfnWriteUserAsync(pImage->pInterfaceIO->pvUser,
225 pImage->pStorage,
226 uOffset, pIoCtx,
227 cbWrite,
228 pfnComplete,
229 pvCompleteUser);
230}
231
232DECLINLINE(int) parallelsFileWriteMetaAsync(PPARALLELSIMAGE pImage, uint64_t uOffset,
233 void *pvBuffer, size_t cbBuffer,
234 PVDIOCTX pIoCtx,
235 PFNVDXFERCOMPLETED pfnComplete,
236 void *pvCompleteUser)
237{
238 return pImage->pInterfaceIOCallbacks->pfnWriteMetaAsync(pImage->pInterfaceIO->pvUser,
239 pImage->pStorage,
240 uOffset, pvBuffer,
241 cbBuffer, pIoCtx,
242 pfnComplete,
243 pvCompleteUser);
244}
245
246DECLINLINE(int) parallelsFileFlushAsync(PPARALLELSIMAGE pImage, PVDIOCTX pIoCtx,
247 PFNVDXFERCOMPLETED pfnComplete,
248 void *pvCompleteUser)
249{
250 return pImage->pInterfaceIOCallbacks->pfnFlushAsync(pImage->pInterfaceIO->pvUser,
251 pImage->pStorage,
252 pIoCtx, pfnComplete,
253 pvCompleteUser);
254}
255
256
257/**
258 * Internal. Flush image data to disk.
259 */
260static int parallelsFlushImage(PPARALLELSIMAGE pImage)
261{
262 int rc = VINF_SUCCESS;
263
264 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
265 return VINF_SUCCESS;
266
267 if ( !(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
268 && (pImage->fAllocationBitmapChanged))
269 {
270 pImage->fAllocationBitmapChanged = false;
271 /* Write the allocation bitmap to the file. */
272 rc = parallelsFileWriteSync(pImage, sizeof(ParallelsHeader),
273 pImage->pAllocationBitmap,
274 pImage->cAllocationBitmapEntries * sizeof(uint32_t),
275 NULL);
276 if (RT_FAILURE(rc))
277 return rc;
278 }
279
280 /* Flush file. */
281 rc = parallelsFileFlushSync(pImage);
282
283 LogFlowFunc(("returns %Rrc\n", rc));
284 return rc;
285}
286
287/**
288 * Internal. Free all allocated space for representing an image except pImage,
289 * and optionally delete the image from disk.
290 */
291static int parallelsFreeImage(PPARALLELSIMAGE pImage, bool fDelete)
292{
293 int rc = VINF_SUCCESS;
294
295 /* Freeing a never allocated image (e.g. because the open failed) is
296 * not signalled as an error. After all nothing bad happens. */
297 if (pImage)
298 {
299 if (pImage->pStorage)
300 {
301 /* No point updating the file that is deleted anyway. */
302 if (!fDelete)
303 parallelsFlushImage(pImage);
304
305 parallelsFileClose(pImage);
306 pImage->pStorage = NULL;
307 }
308
309 if (pImage->pAllocationBitmap)
310 {
311 RTMemFree(pImage->pAllocationBitmap);
312 pImage->pAllocationBitmap = NULL;
313 }
314
315 if (fDelete && pImage->pszFilename)
316 parallelsFileDelete(pImage, pImage->pszFilename);
317 }
318
319 return rc;
320}
321
322static int parallelsOpenImage(PPARALLELSIMAGE pImage, unsigned uOpenFlags)
323{
324 int rc = VINF_SUCCESS;
325 ParallelsHeader parallelsHeader;
326
327 /* Try to get error interface. */
328 pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
329 if (pImage->pInterfaceError)
330 pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
331
332 /* Get I/O interface. */
333 pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
334 AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
335 pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
336 AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
337
338 rc = parallelsFileOpen(pImage, pImage->pszFilename,
339 VDOpenFlagsToFileOpenFlags(uOpenFlags,
340 false /* fCreate */));
341 if (RT_FAILURE(rc))
342 goto out;
343
344 rc = parallelsFileGetSize(pImage, &pImage->cbFileCurrent);
345 if (RT_FAILURE(rc))
346 goto out;
347 AssertMsg(pImage->cbFileCurrent % 512 == 0, ("File size is not a multiple of 512\n"));
348
349 rc = parallelsFileReadSync(pImage, 0, &parallelsHeader, sizeof(parallelsHeader), NULL);
350 if (RT_FAILURE(rc))
351 goto out;
352
353 if (memcmp(parallelsHeader.HeaderIdentifier, PARALLELS_HEADER_MAGIC, 16))
354 {
355 /* Check if the file has hdd as extension. It is a fixed size raw image then. */
356 char *pszExtension = RTPathExt(pImage->pszFilename);
357 if (strcmp(pszExtension, ".hdd"))
358 {
359 rc = VERR_VD_PARALLELS_INVALID_HEADER;
360 goto out;
361 }
362
363 /* This is a fixed size image. */
364 pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
365 pImage->cbSize = pImage->cbFileCurrent;
366
367 pImage->PCHSGeometry.cHeads = 16;
368 pImage->PCHSGeometry.cSectors = 63;
369 uint64_t cCylinders = pImage->cbSize / (512 * pImage->PCHSGeometry.cSectors * pImage->PCHSGeometry.cHeads);
370 pImage->PCHSGeometry.cCylinders = (uint32_t)cCylinders;
371 }
372 else
373 {
374 if (parallelsHeader.uVersion != PARALLELS_DISK_VERSION)
375 {
376 rc = VERR_NOT_SUPPORTED;
377 goto out;
378 }
379
380 if (parallelsHeader.cEntriesInAllocationBitmap > (1 << 30))
381 {
382 rc = VERR_NOT_SUPPORTED;
383 goto out;
384 }
385
386 Log(("cSectors=%u\n", parallelsHeader.cSectors));
387 pImage->cbSize = ((uint64_t)parallelsHeader.cSectors) * 512;
388 pImage->uImageFlags = VD_IMAGE_FLAGS_NONE;
389 pImage->cAllocationBitmapEntries = parallelsHeader.cEntriesInAllocationBitmap;
390 pImage->pAllocationBitmap = (uint32_t *)RTMemAllocZ((uint32_t)pImage->cAllocationBitmapEntries * sizeof(uint32_t));
391 if (!pImage->pAllocationBitmap)
392 {
393 rc = VERR_NO_MEMORY;
394 goto out;
395 }
396
397 rc = parallelsFileReadSync(pImage, sizeof(ParallelsHeader),
398 pImage->pAllocationBitmap,
399 pImage->cAllocationBitmapEntries * sizeof(uint32_t),
400 NULL);
401 if (RT_FAILURE(rc))
402 goto out;
403
404 pImage->PCHSGeometry.cCylinders = parallelsHeader.cCylinders;
405 pImage->PCHSGeometry.cHeads = parallelsHeader.cHeads;
406 pImage->PCHSGeometry.cSectors = parallelsHeader.cSectorsPerTrack;
407 }
408
409out:
410 LogFlowFunc(("returns %Rrc\n", rc));
411 return rc;
412}
413
414/**
415 * Internal: Create a parallels image.
416 */
417static int parallelsCreateImage(PPARALLELSIMAGE pImage, uint64_t cbSize,
418 unsigned uImageFlags, const char *pszComment,
419 PCVDGEOMETRY pPCHSGeometry,
420 PCVDGEOMETRY pLCHSGeometry, unsigned uOpenFlags,
421 PFNVDPROGRESS pfnProgress, void *pvUser,
422 unsigned uPercentStart, unsigned uPercentSpan)
423{
424 int rc = VINF_SUCCESS;
425 int32_t fOpen;
426
427 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
428 {
429 rc = parallelsError(pImage, VERR_VD_INVALID_TYPE, RT_SRC_POS, N_("Parallels: cannot create fixed image '%s'. Create a raw image"), pImage->pszFilename);
430 goto out;
431 }
432
433 pImage->uOpenFlags = uOpenFlags & ~VD_OPEN_FLAGS_READONLY;
434 pImage->uImageFlags = uImageFlags;
435 pImage->PCHSGeometry = *pPCHSGeometry;
436 pImage->LCHSGeometry = *pLCHSGeometry;
437
438 if (!pImage->PCHSGeometry.cCylinders)
439 {
440 /* Set defaults. */
441 pImage->PCHSGeometry.cSectors = 63;
442 pImage->PCHSGeometry.cHeads = 16;
443 pImage->PCHSGeometry.cCylinders = pImage->cbSize / (512 * pImage->PCHSGeometry.cSectors * pImage->PCHSGeometry.cHeads);
444 }
445
446 pImage->pInterfaceError = VDInterfaceGet(pImage->pVDIfsDisk, VDINTERFACETYPE_ERROR);
447 if (pImage->pInterfaceError)
448 pImage->pInterfaceErrorCallbacks = VDGetInterfaceError(pImage->pInterfaceError);
449
450 /* Get I/O interface. */
451 pImage->pInterfaceIO = VDInterfaceGet(pImage->pVDIfsImage, VDINTERFACETYPE_IOINT);
452 AssertPtrReturn(pImage->pInterfaceIO, VERR_INVALID_PARAMETER);
453 pImage->pInterfaceIOCallbacks = VDGetInterfaceIOInt(pImage->pInterfaceIO);
454 AssertPtrReturn(pImage->pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
455
456 /* Create image file. */
457 fOpen = VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags, true /* fCreate */);
458 rc = parallelsFileOpen(pImage, pImage->pszFilename, fOpen);
459 if (RT_FAILURE(rc))
460 {
461 rc = parallelsError(pImage, rc, RT_SRC_POS, N_("Parallels: cannot create image '%s'"), pImage->pszFilename);
462 goto out;
463 }
464
465 if (RT_SUCCESS(rc) && pfnProgress)
466 pfnProgress(pvUser, uPercentStart + uPercentSpan * 98 / 100);
467
468 /* Setup image state. */
469 pImage->cbSize = cbSize;
470 pImage->cAllocationBitmapEntries = cbSize / 512 / pImage->PCHSGeometry.cSectors;
471 if (pImage->cAllocationBitmapEntries * pImage->PCHSGeometry.cSectors * 512 < cbSize)
472 pImage->cAllocationBitmapEntries++;
473 pImage->fAllocationBitmapChanged = true;
474 pImage->cbFileCurrent = sizeof(ParallelsHeader) + pImage->cAllocationBitmapEntries * sizeof(uint32_t);
475 /* Round to next sector boundary. */
476 pImage->cbFileCurrent += 512 - pImage->cbFileCurrent % 512;
477 Assert(!(pImage->cbFileCurrent % 512));
478 pImage->pAllocationBitmap = (uint32_t *)RTMemAllocZ(pImage->cAllocationBitmapEntries * sizeof(uint32_t));
479 if (!pImage->pAllocationBitmap)
480 rc = VERR_NO_MEMORY;
481
482 if (RT_SUCCESS(rc))
483 {
484 ParallelsHeader Header;
485
486 memcpy(Header.HeaderIdentifier, PARALLELS_HEADER_MAGIC, sizeof(Header.HeaderIdentifier));
487 Header.uVersion = RT_H2LE_U32(PARALLELS_DISK_VERSION);
488 Header.cHeads = RT_H2LE_U32(pImage->PCHSGeometry.cHeads);
489 Header.cCylinders = RT_H2LE_U32(pImage->PCHSGeometry.cCylinders);
490 Header.cSectorsPerTrack = RT_H2LE_U32(pImage->PCHSGeometry.cSectors);
491 Header.cEntriesInAllocationBitmap = RT_H2LE_U32(pImage->cAllocationBitmapEntries);
492 Header.cSectors = RT_H2LE_U32(pImage->cbSize / 512);
493 memset(Header.Padding, 0, sizeof(Header.Padding));
494
495 /* Write header and allocation bitmap. */
496 rc = parallelsFileSetSize(pImage, pImage->cbFileCurrent);
497 if (RT_SUCCESS(rc))
498 rc = parallelsFileWriteSync(pImage, 0, &Header, sizeof(Header), NULL);
499 if (RT_SUCCESS(rc))
500 rc = parallelsFlushImage(pImage); /* Writes the allocation bitmap. */
501 }
502
503out:
504 if (RT_SUCCESS(rc) && pfnProgress)
505 pfnProgress(pvUser, uPercentStart + uPercentSpan);
506
507 if (RT_FAILURE(rc))
508 parallelsFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
509 return rc;
510}
511
512/** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
513static int parallelsCheckIfValid(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
514 PVDINTERFACE pVDIfsImage, VDTYPE *penmType)
515{
516 int rc;
517 PVDIOSTORAGE pStorage;
518 ParallelsHeader parallelsHeader;
519
520 /* Get I/O interface. */
521 PVDINTERFACE pInterfaceIO = VDInterfaceGet(pVDIfsImage, VDINTERFACETYPE_IOINT);
522 AssertPtrReturn(pInterfaceIO, VERR_INVALID_PARAMETER);
523 PVDINTERFACEIOINT pInterfaceIOCallbacks = VDGetInterfaceIOInt(pInterfaceIO);
524 AssertPtrReturn(pInterfaceIOCallbacks, VERR_INVALID_PARAMETER);
525
526 rc = pInterfaceIOCallbacks->pfnOpen(pInterfaceIO->pvUser, pszFilename,
527 VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
528 false /* fCreate */),
529 &pStorage);
530 if (RT_FAILURE(rc))
531 return rc;
532
533 rc = pInterfaceIOCallbacks->pfnReadSync(pInterfaceIO->pvUser, pStorage,
534 0, &parallelsHeader,
535 sizeof(ParallelsHeader), NULL);
536 if (RT_SUCCESS(rc))
537 {
538 if ( !memcmp(parallelsHeader.HeaderIdentifier, PARALLELS_HEADER_MAGIC, 16)
539 && (parallelsHeader.uVersion == PARALLELS_DISK_VERSION))
540 rc = VINF_SUCCESS;
541 else
542 {
543 /*
544 * The image may be an fixed size image.
545 * Unfortunately fixed sized parallels images
546 * are just raw files hence no magic header to
547 * check for.
548 * The code succeeds if the file is a multiple
549 * of 512 and if the file extensions is *.hdd
550 */
551 uint64_t cbFile;
552 char *pszExtension;
553
554 rc = pInterfaceIOCallbacks->pfnGetSize(pInterfaceIO->pvUser, pStorage,
555 &cbFile);
556 if (RT_FAILURE(rc) || ((cbFile % 512) != 0))
557 {
558 pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
559 return VERR_VD_PARALLELS_INVALID_HEADER;
560 }
561
562 pszExtension = RTPathExt(pszFilename);
563 if (!pszExtension || strcmp(pszExtension, ".hdd"))
564 rc = VERR_VD_PARALLELS_INVALID_HEADER;
565 else
566 rc = VINF_SUCCESS;
567 }
568 }
569
570 if (RT_SUCCESS(rc))
571 *penmType = VDTYPE_HDD;
572
573 pInterfaceIOCallbacks->pfnClose(pInterfaceIO->pvUser, pStorage);
574 return rc;
575}
576
577/** @copydoc VBOXHDDBACKEND::pfnOpen */
578static int parallelsOpen(const char *pszFilename, unsigned uOpenFlags,
579 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
580 VDTYPE enmType, void **ppBackendData)
581{
582 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, ppBackendData));
583 int rc;
584 PPARALLELSIMAGE pImage;
585
586 /* Check open flags. All valid flags are supported. */
587 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
588 {
589 rc = VERR_INVALID_PARAMETER;
590 goto out;
591 }
592
593 /* Check remaining arguments. */
594 if ( !VALID_PTR(pszFilename)
595 || !*pszFilename)
596 {
597 rc = VERR_INVALID_PARAMETER;
598 goto out;
599 }
600
601 pImage = (PPARALLELSIMAGE)RTMemAllocZ(sizeof(PARALLELSIMAGE));
602 if (!pImage)
603 {
604 rc = VERR_NO_MEMORY;
605 goto out;
606 }
607
608 pImage->pszFilename = pszFilename;
609 pImage->pStorage = NULL;
610 pImage->pVDIfsDisk = pVDIfsDisk;
611 pImage->pVDIfsImage = pVDIfsImage;
612 pImage->fAllocationBitmapChanged = false;
613
614 rc = parallelsOpenImage(pImage, uOpenFlags);
615 if (RT_SUCCESS(rc))
616 *ppBackendData = pImage;
617 else
618 RTMemFree(pImage);
619
620out:
621 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
622 return rc;
623}
624
625/** @copydoc VBOXHDDBACKEND::pfnCreate */
626static int parallelsCreate(const char *pszFilename, uint64_t cbSize,
627 unsigned uImageFlags, const char *pszComment,
628 PCVDGEOMETRY pPCHSGeometry,
629 PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
630 unsigned uOpenFlags, unsigned uPercentStart,
631 unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
632 PVDINTERFACE pVDIfsImage,
633 PVDINTERFACE pVDIfsOperation, void **ppBackendData)
634{
635 LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p ppBackendData=%#p",
636 pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, ppBackendData));
637 int rc = VINF_SUCCESS;
638 PPARALLELSIMAGE pImage;
639
640 PFNVDPROGRESS pfnProgress = NULL;
641 void *pvUser = NULL;
642 PVDINTERFACE pIfProgress = VDInterfaceGet(pVDIfsOperation,
643 VDINTERFACETYPE_PROGRESS);
644 PVDINTERFACEPROGRESS pCbProgress = NULL;
645 if (pIfProgress)
646 {
647 pCbProgress = VDGetInterfaceProgress(pIfProgress);
648 if (pCbProgress)
649 pfnProgress = pCbProgress->pfnProgress;
650 pvUser = pIfProgress->pvUser;
651 }
652
653 /* Check open flags. All valid flags are supported. */
654 if (uOpenFlags & ~VD_OPEN_FLAGS_MASK)
655 {
656 rc = VERR_INVALID_PARAMETER;
657 goto out;
658 }
659
660 /* Check remaining arguments. */
661 if ( !VALID_PTR(pszFilename)
662 || !*pszFilename
663 || !VALID_PTR(pPCHSGeometry)
664 || !VALID_PTR(pLCHSGeometry))
665 {
666 rc = VERR_INVALID_PARAMETER;
667 goto out;
668 }
669
670 pImage = (PPARALLELSIMAGE)RTMemAllocZ(sizeof(PARALLELSIMAGE));
671 if (!pImage)
672 {
673 rc = VERR_NO_MEMORY;
674 goto out;
675 }
676 pImage->pszFilename = pszFilename;
677 pImage->pStorage = NULL;
678 pImage->pVDIfsDisk = pVDIfsDisk;
679 pImage->pVDIfsImage = pVDIfsImage;
680
681 rc = parallelsCreateImage(pImage, cbSize, uImageFlags, pszComment,
682 pPCHSGeometry, pLCHSGeometry, uOpenFlags,
683 pfnProgress, pvUser, uPercentStart, uPercentSpan);
684 if (RT_SUCCESS(rc))
685 {
686 /* So far the image is opened in read/write mode. Make sure the
687 * image is opened in read-only mode if the caller requested that. */
688 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
689 {
690 parallelsFreeImage(pImage, false);
691 rc = parallelsOpenImage(pImage, uOpenFlags);
692 if (RT_FAILURE(rc))
693 {
694 RTMemFree(pImage);
695 goto out;
696 }
697 }
698 *ppBackendData = pImage;
699 }
700 else
701 RTMemFree(pImage);
702
703out:
704 LogFlowFunc(("returns %Rrc\n", rc));
705 return rc;
706}
707
708/** @copydoc VBOXHDDBACKEND::pfnRename */
709static int parallelsRename(void *pBackendData, const char *pszFilename)
710{
711 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
712 int rc = VINF_SUCCESS;
713 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
714
715 /* Check arguments. */
716 if ( !pImage
717 || !pszFilename
718 || !*pszFilename)
719 {
720 rc = VERR_INVALID_PARAMETER;
721 goto out;
722 }
723
724 /* Close the image. */
725 rc = parallelsFreeImage(pImage, false);
726 if (RT_FAILURE(rc))
727 goto out;
728
729 /* Rename the file. */
730 rc = parallelsFileMove(pImage, pImage->pszFilename, pszFilename, 0);
731 if (RT_FAILURE(rc))
732 {
733 /* The move failed, try to reopen the original image. */
734 int rc2 = parallelsOpenImage(pImage, pImage->uOpenFlags);
735 if (RT_FAILURE(rc2))
736 rc = rc2;
737
738 goto out;
739 }
740
741 /* Update pImage with the new information. */
742 pImage->pszFilename = pszFilename;
743
744 /* Open the old image with new name. */
745 rc = parallelsOpenImage(pImage, pImage->uOpenFlags);
746 if (RT_FAILURE(rc))
747 goto out;
748
749out:
750 LogFlowFunc(("returns %Rrc\n", rc));
751 return rc;
752}
753
754/** @copydoc VBOXHDDBACKEND::pfnClose */
755static int parallelsClose(void *pBackendData, bool fDelete)
756{
757 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
758 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
759 int rc;
760
761 rc = parallelsFreeImage(pImage, fDelete);
762 RTMemFree(pImage);
763
764 LogFlowFunc(("returns %Rrc\n", rc));
765 return rc;
766}
767
768/** @copydoc VBOXHDDBACKEND::pfnRead */
769static int parallelsRead(void *pBackendData, uint64_t uOffset, void *pvBuf,
770 size_t cbToRead, size_t *pcbActuallyRead)
771{
772 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
773 pBackendData, uOffset, pvBuf, cbToRead, pcbActuallyRead));
774 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
775 int rc = VINF_SUCCESS;
776
777 AssertPtr(pImage);
778 Assert(uOffset % 512 == 0);
779 Assert(cbToRead % 512 == 0);
780
781 if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
782 rc = parallelsFileReadSync(pImage, uOffset, pvBuf, cbToRead, NULL);
783 else
784 {
785 uint64_t uSector;
786 uint32_t iIndexInAllocationTable;
787
788 /* Calculate offset in the real file. */
789 uSector = uOffset / 512;
790
791 /* One chunk in the file is always one track big. */
792 iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
793 uSector = uSector % pImage->PCHSGeometry.cSectors;
794
795 Assert(iIndexInAllocationTable < pImage->cAllocationBitmapEntries);
796
797 cbToRead = RT_MIN(cbToRead, (pImage->PCHSGeometry.cSectors - uSector)*512);
798
799 LogFlowFunc(("AllocationBitmap[%u]=%u uSector=%u cbToRead=%zu cAllocationBitmapEntries=%u\n",
800 iIndexInAllocationTable, pImage->pAllocationBitmap[iIndexInAllocationTable],
801 uSector, cbToRead, pImage->cAllocationBitmapEntries));
802
803 if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
804 rc = VERR_VD_BLOCK_FREE;
805 else
806 {
807 uint64_t uOffsetInFile = ((uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
808
809 LogFlowFunc(("uOffsetInFile=%llu\n", uOffsetInFile));
810 rc = parallelsFileReadSync(pImage, uOffsetInFile, pvBuf, cbToRead, NULL);
811 }
812 }
813
814 if ( ( RT_SUCCESS(rc)
815 || rc == VERR_VD_BLOCK_FREE)
816 && pcbActuallyRead)
817 *pcbActuallyRead = cbToRead;
818
819 LogFlowFunc(("returns %Rrc\n", rc));
820 return rc;
821}
822
823/** @copydoc VBOXHDDBACKEND::pfnWrite */
824static int parallelsWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf,
825 size_t cbToWrite, size_t *pcbWriteProcess,
826 size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite)
827{
828 LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p\n",
829 pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess));
830 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
831 int rc = VINF_SUCCESS;
832
833 AssertPtr(pImage);
834 Assert(uOffset % 512 == 0);
835 Assert(cbToWrite % 512 == 0);
836
837 if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
838 rc = parallelsFileWriteSync(pImage, uOffset, pvBuf, cbToWrite, NULL);
839 else
840 {
841 uint64_t uSector;
842 uint64_t uOffsetInFile;
843 uint32_t iIndexInAllocationTable;
844
845 /* Calculate offset in the real file. */
846 uSector = uOffset / 512;
847 /* One chunk in the file is always one track big. */
848 iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
849 uSector = uSector % pImage->PCHSGeometry.cSectors;
850
851 Assert(iIndexInAllocationTable < pImage->cAllocationBitmapEntries);
852
853 cbToWrite = RT_MIN(cbToWrite, (pImage->PCHSGeometry.cSectors - uSector)*512);
854
855 LogFlowFunc(("AllocationBitmap[%u]=%u uSector=%u cbToWrite=%zu cAllocationBitmapEntries=%u\n",
856 iIndexInAllocationTable, pImage->pAllocationBitmap[iIndexInAllocationTable],
857 uSector, cbToWrite, pImage->cAllocationBitmapEntries));
858
859 if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
860 {
861 if ( cbToWrite == pImage->PCHSGeometry.cSectors * 512
862 && !(fWrite & VD_WRITE_NO_ALLOC))
863 {
864 /* Stay on the safe side. Do not run the risk of confusing the higher
865 * level, as that can be pretty lethal to image consistency. */
866 *pcbPreRead = 0;
867 *pcbPostRead = 0;
868
869 /* Allocate new chunk in the file. */
870 AssertMsg(pImage->cbFileCurrent % 512 == 0, ("File size is not a multiple of 512\n"));
871 pImage->pAllocationBitmap[iIndexInAllocationTable] = (uint32_t)(pImage->cbFileCurrent / 512);
872 pImage->cbFileCurrent += pImage->PCHSGeometry.cSectors * 512;
873 pImage->fAllocationBitmapChanged = true;
874
875 uOffsetInFile = (uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] * 512;
876
877 LogFlowFunc(("uOffsetInFile=%llu\n", uOffsetInFile));
878
879 /*
880 * Write the new block at the current end of the file.
881 */
882 rc = parallelsFileWriteSync(pImage, uOffsetInFile, pvBuf, cbToWrite,
883 NULL);
884 }
885 else
886 {
887 /* Trying to do a partial write to an unallocated cluster. Don't do
888 * anything except letting the upper layer know what to do. */
889 *pcbPreRead = uSector * 512;
890 *pcbPostRead = (pImage->PCHSGeometry.cSectors * 512) - cbToWrite - *pcbPreRead;
891 rc = VERR_VD_BLOCK_FREE;
892 }
893 }
894 else
895 {
896 uOffsetInFile = ((uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
897
898 LogFlowFunc(("uOffsetInFile=%llu\n", uOffsetInFile));
899 rc = parallelsFileWriteSync(pImage, uOffsetInFile, pvBuf, cbToWrite, NULL);
900 }
901 }
902
903 if (pcbWriteProcess)
904 *pcbWriteProcess = cbToWrite;
905
906out:
907 LogFlowFunc(("returns %Rrc\n", rc));
908 return rc;
909}
910
911/** @copydoc VBOXHDDBACKEND::pfnFlush */
912static int parallelsFlush(void *pBackendData)
913{
914 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
915 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
916 int rc;
917
918 AssertPtr(pImage);
919
920 rc = parallelsFlushImage(pImage);
921
922 LogFlowFunc(("returns %Rrc\n", rc));
923 return rc;
924}
925
926/** @copydoc VBOXHDDBACKEND::pfnGetVersion */
927static unsigned parallelsGetVersion(void *pBackendData)
928{
929 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
930 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
931
932 AssertPtr(pImage);
933
934 if (pImage)
935 return PARALLELS_DISK_VERSION;
936 else
937 return 0;
938}
939
940/** @copydoc VBOXHDDBACKEND::pfnGetSize */
941static uint64_t parallelsGetSize(void *pBackendData)
942{
943 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
944 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
945 uint64_t cb = 0;
946
947 AssertPtr(pImage);
948
949 if (pImage && pImage->pStorage)
950 cb = pImage->cbSize;
951
952 LogFlowFunc(("returns %llu\n", cb));
953 return cb;
954}
955
956/** @copydoc VBOXHDDBACKEND::pfnGetFileSize */
957static uint64_t parallelsGetFileSize(void *pBackendData)
958{
959 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
960 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
961 uint64_t cb = 0;
962
963 AssertPtr(pImage);
964
965 if (pImage && pImage->pStorage)
966 cb = pImage->cbFileCurrent;
967
968 LogFlowFunc(("returns %lld\n", cb));
969 return cb;
970}
971
972/** @copydoc VBOXHDDBACKEND::pfnGetPCHSGeometry */
973static int parallelsGetPCHSGeometry(void *pBackendData,
974 PVDGEOMETRY pPCHSGeometry)
975{
976 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
977 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
978 int rc;
979
980 AssertPtr(pImage);
981
982 if (pImage)
983 {
984 if (pImage->PCHSGeometry.cCylinders)
985 {
986 *pPCHSGeometry = pImage->PCHSGeometry;
987 rc = VINF_SUCCESS;
988 }
989 else
990 rc = VERR_VD_GEOMETRY_NOT_SET;
991 }
992 else
993 rc = VERR_VD_NOT_OPENED;
994
995 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
996 return rc;
997}
998
999/** @copydoc VBOXHDDBACKEND::pfnSetPCHSGeometry */
1000static int parallelsSetPCHSGeometry(void *pBackendData,
1001 PCVDGEOMETRY pPCHSGeometry)
1002{
1003 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1004 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1005 int rc;
1006
1007 AssertPtr(pImage);
1008
1009 if (pImage)
1010 {
1011 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1012 {
1013 rc = VERR_VD_IMAGE_READ_ONLY;
1014 goto out;
1015 }
1016
1017 pImage->PCHSGeometry = *pPCHSGeometry;
1018 rc = VINF_SUCCESS;
1019 }
1020 else
1021 rc = VERR_VD_NOT_OPENED;
1022
1023out:
1024 LogFlowFunc(("returns %Rrc\n", rc));
1025 return rc;
1026}
1027
1028/** @copydoc VBOXHDDBACKEND::pfnGetLCHSGeometry */
1029static int parallelsGetLCHSGeometry(void *pBackendData,
1030 PVDGEOMETRY pLCHSGeometry)
1031{
1032 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
1033 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1034 int rc;
1035
1036 AssertPtr(pImage);
1037
1038 if (pImage)
1039 {
1040 if (pImage->LCHSGeometry.cCylinders)
1041 {
1042 *pLCHSGeometry = pImage->LCHSGeometry;
1043 rc = VINF_SUCCESS;
1044 }
1045 else
1046 rc = VERR_VD_GEOMETRY_NOT_SET;
1047 }
1048 else
1049 rc = VERR_VD_NOT_OPENED;
1050
1051 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1052 return rc;
1053}
1054
1055/** @copydoc VBOXHDDBACKEND::pfnSetLCHSGeometry */
1056static int parallelsSetLCHSGeometry(void *pBackendData,
1057 PCVDGEOMETRY pLCHSGeometry)
1058{
1059 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
1060 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1061 int rc;
1062
1063 AssertPtr(pImage);
1064
1065 if (pImage)
1066 {
1067 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1068 {
1069 rc = VERR_VD_IMAGE_READ_ONLY;
1070 goto out;
1071 }
1072
1073 pImage->LCHSGeometry = *pLCHSGeometry;
1074 rc = VINF_SUCCESS;
1075 }
1076 else
1077 rc = VERR_VD_NOT_OPENED;
1078
1079out:
1080 LogFlowFunc(("returns %Rrc\n", rc));
1081 return rc;
1082}
1083
1084/** @copydoc VBOXHDDBACKEND::pfnGetImageFlags */
1085static unsigned parallelsGetImageFlags(void *pBackendData)
1086{
1087 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1088 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1089 unsigned uImageFlags;
1090
1091 AssertPtr(pImage);
1092
1093 if (pImage)
1094 uImageFlags = pImage->uImageFlags;
1095 else
1096 uImageFlags = 0;
1097
1098 LogFlowFunc(("returns %#x\n", uImageFlags));
1099 return uImageFlags;
1100}
1101
1102/** @copydoc VBOXHDDBACKEND::pfnGetOpenFlags */
1103static unsigned parallelsGetOpenFlags(void *pBackendData)
1104{
1105 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
1106 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1107 unsigned uOpenFlags;
1108
1109 AssertPtr(pImage);
1110
1111 if (pImage)
1112 uOpenFlags = pImage->uOpenFlags;
1113 else
1114 uOpenFlags = 0;
1115
1116 LogFlowFunc(("returns %#x\n", uOpenFlags));
1117 return uOpenFlags;
1118}
1119
1120/** @copydoc VBOXHDDBACKEND::pfnSetOpenFlags */
1121static int parallelsSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
1122{
1123 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
1124 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1125 int rc;
1126
1127 /* Image must be opened and the new flags must be valid. */
1128 if (!pImage || (uOpenFlags & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_SHAREABLE | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_ASYNC_IO)))
1129 {
1130 rc = VERR_INVALID_PARAMETER;
1131 goto out;
1132 }
1133
1134 /* Implement this operation via reopening the image. */
1135 parallelsFreeImage(pImage, false);
1136 rc = parallelsOpenImage(pImage, uOpenFlags);
1137
1138out:
1139 LogFlowFunc(("returns %Rrc\n", rc));
1140 return rc;
1141}
1142
1143/** @copydoc VBOXHDDBACKEND::pfnGetComment */
1144static int parallelsGetComment(void *pBackendData, char *pszComment,
1145 size_t cbComment)
1146{
1147 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
1148 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1149 int rc;
1150
1151 AssertPtr(pImage);
1152
1153 if (pImage)
1154 rc = VERR_NOT_SUPPORTED;
1155 else
1156 rc = VERR_VD_NOT_OPENED;
1157
1158 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
1159 return rc;
1160}
1161
1162/** @copydoc VBOXHDDBACKEND::pfnSetComment */
1163static int parallelsSetComment(void *pBackendData, const char *pszComment)
1164{
1165 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
1166 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1167 int rc;
1168
1169 AssertPtr(pImage);
1170
1171 if (pImage)
1172 {
1173 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1174 rc = VERR_VD_IMAGE_READ_ONLY;
1175 else
1176 rc = VERR_NOT_SUPPORTED;
1177 }
1178 else
1179 rc = VERR_VD_NOT_OPENED;
1180
1181 LogFlowFunc(("returns %Rrc\n", rc));
1182 return rc;
1183}
1184
1185/** @copydoc VBOXHDDBACKEND::pfnGetUuid */
1186static int parallelsGetUuid(void *pBackendData, PRTUUID pUuid)
1187{
1188 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1189 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1190 int rc;
1191
1192 AssertPtr(pImage);
1193
1194 if (pImage)
1195 rc = VERR_NOT_SUPPORTED;
1196 else
1197 rc = VERR_VD_NOT_OPENED;
1198
1199 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
1200 return rc;
1201}
1202
1203/** @copydoc VBOXHDDBACKEND::pfnSetUuid */
1204static int parallelsSetUuid(void *pBackendData, PCRTUUID pUuid)
1205{
1206 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1207 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1208 int rc;
1209
1210 AssertPtr(pImage);
1211
1212 if (pImage)
1213 {
1214 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1215 rc = VERR_NOT_SUPPORTED;
1216 else
1217 rc = VERR_VD_IMAGE_READ_ONLY;
1218 }
1219 else
1220 rc = VERR_VD_NOT_OPENED;
1221
1222 LogFlowFunc(("returns %Rrc\n", rc));
1223 return rc;
1224}
1225
1226/** @copydoc VBOXHDDBACKEND::pfnGetModificationUuid */
1227static int parallelsGetModificationUuid(void *pBackendData, PRTUUID pUuid)
1228{
1229 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1230 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1231 int rc;
1232
1233 AssertPtr(pImage);
1234
1235 if (pImage)
1236 rc = VERR_NOT_SUPPORTED;
1237 else
1238 rc = VERR_VD_NOT_OPENED;
1239
1240 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
1241 return rc;
1242}
1243
1244/** @copydoc VBOXHDDBACKEND::pfnSetModificationUuid */
1245static int parallelsSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
1246{
1247 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1248 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1249 int rc;
1250
1251 AssertPtr(pImage);
1252
1253 if (pImage)
1254 {
1255 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1256 rc = VERR_NOT_SUPPORTED;
1257 else
1258 rc = VERR_VD_IMAGE_READ_ONLY;
1259 }
1260 else
1261 rc = VERR_VD_NOT_OPENED;
1262
1263 LogFlowFunc(("returns %Rrc\n", rc));
1264 return rc;
1265}
1266
1267/** @copydoc VBOXHDDBACKEND::pfnGetParentUuid */
1268static int parallelsGetParentUuid(void *pBackendData, PRTUUID pUuid)
1269{
1270 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1271 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1272 int rc;
1273
1274 AssertPtr(pImage);
1275
1276 if (pImage)
1277 rc = VERR_NOT_SUPPORTED;
1278 else
1279 rc = VERR_VD_NOT_OPENED;
1280
1281 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
1282 return rc;
1283}
1284
1285/** @copydoc VBOXHDDBACKEND::pfnSetParentUuid */
1286static int parallelsSetParentUuid(void *pBackendData, PCRTUUID pUuid)
1287{
1288 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1289 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1290 int rc;
1291
1292 AssertPtr(pImage);
1293
1294 if (pImage)
1295 {
1296 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1297 rc = VERR_NOT_SUPPORTED;
1298 else
1299 rc = VERR_VD_IMAGE_READ_ONLY;
1300 }
1301 else
1302 rc = VERR_VD_NOT_OPENED;
1303
1304 LogFlowFunc(("returns %Rrc\n", rc));
1305 return rc;
1306}
1307
1308/** @copydoc VBOXHDDBACKEND::pfnGetParentModificationUuid */
1309static int parallelsGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
1310{
1311 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1312 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1313 int rc;
1314
1315 AssertPtr(pImage);
1316
1317 if (pImage)
1318 rc = VERR_NOT_SUPPORTED;
1319 else
1320 rc = VERR_VD_NOT_OPENED;
1321
1322 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
1323 return rc;
1324}
1325
1326/** @copydoc VBOXHDDBACKEND::pfnSetParentModificationUuid */
1327static int parallelsSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
1328{
1329 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1330 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1331 int rc;
1332
1333 AssertPtr(pImage);
1334
1335 if (pImage)
1336 {
1337 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
1338 rc = VERR_NOT_SUPPORTED;
1339 else
1340 rc = VERR_VD_IMAGE_READ_ONLY;
1341 }
1342 else
1343 rc = VERR_VD_NOT_OPENED;
1344
1345 LogFlowFunc(("returns %Rrc\n", rc));
1346 return rc;
1347}
1348
1349/** @copydoc VBOXHDDBACKEND::pfnDump */
1350static void parallelsDump(void *pBackendData)
1351{
1352 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1353
1354 AssertPtr(pImage);
1355 if (pImage)
1356 {
1357 parallelsMessage(pImage, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u\n",
1358 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
1359 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors);
1360 }
1361}
1362
1363/** @copydoc VBOXHDDBACKEND::pfnAsyncRead */
1364static int parallelsAsyncRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
1365 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
1366{
1367 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
1368 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
1369 int rc = VINF_SUCCESS;
1370 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1371 uint64_t uSector;
1372 uint64_t uOffsetInFile;
1373 uint32_t iIndexInAllocationTable;
1374
1375 AssertPtr(pImage);
1376 Assert(uOffset % 512 == 0);
1377 Assert(cbToRead % 512 == 0);
1378
1379 if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
1380 {
1381 rc = parallelsFileReadUserAsync(pImage, uOffset, pIoCtx, cbToRead);
1382 }
1383 else
1384 {
1385 /* Calculate offset in the real file. */
1386 uSector = uOffset / 512;
1387 /* One chunk in the file is always one track big. */
1388 iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
1389 uSector = uSector % pImage->PCHSGeometry.cSectors;
1390
1391 cbToRead = RT_MIN(cbToRead, (pImage->PCHSGeometry.cSectors - uSector)*512);
1392
1393 if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
1394 {
1395 rc = VERR_VD_BLOCK_FREE;
1396 }
1397 else
1398 {
1399 uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
1400 rc = parallelsFileReadUserAsync(pImage, uOffsetInFile, pIoCtx, cbToRead);
1401 }
1402 }
1403
1404 *pcbActuallyRead = cbToRead;
1405
1406 LogFlowFunc(("returns %Rrc\n", rc));
1407 return rc;
1408}
1409
1410/** @copydoc VBOXHDDBACKEND::pfnAsyncWrite */
1411static int parallelsAsyncWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
1412 PVDIOCTX pIoCtx,
1413 size_t *pcbWriteProcess, size_t *pcbPreRead,
1414 size_t *pcbPostRead, unsigned fWrite)
1415{
1416 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p\n",
1417 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess));
1418 int rc = VINF_SUCCESS;
1419 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1420 uint64_t uSector;
1421 uint64_t uOffsetInFile;
1422 uint32_t iIndexInAllocationTable;
1423
1424 AssertPtr(pImage);
1425 Assert(uOffset % 512 == 0);
1426 Assert(cbToWrite % 512 == 0);
1427
1428 if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
1429 {
1430 rc = parallelsFileWriteUserAsync(pImage, uOffset, pIoCtx, cbToWrite, NULL, NULL);
1431 }
1432 else
1433 {
1434 /* Calculate offset in the real file. */
1435 uSector = uOffset / 512;
1436 /* One chunk in the file is always one track big. */
1437 iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
1438 uSector = uSector % pImage->PCHSGeometry.cSectors;
1439
1440 cbToWrite = RT_MIN(cbToWrite, (pImage->PCHSGeometry.cSectors - uSector)*512);
1441
1442 if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
1443 {
1444 if (fWrite & VD_WRITE_NO_ALLOC)
1445 {
1446 *pcbPreRead = uSector * 512;
1447 *pcbPostRead = pImage->PCHSGeometry.cSectors * 512 - cbToWrite - *pcbPreRead;
1448
1449 if (pcbWriteProcess)
1450 *pcbWriteProcess = cbToWrite;
1451 return VERR_VD_BLOCK_FREE;
1452 }
1453
1454 /* Allocate new chunk in the file. */
1455 Assert(uSector == 0);
1456 AssertMsg(pImage->cbFileCurrent % 512 == 0, ("File size is not a multiple of 512\n"));
1457 pImage->pAllocationBitmap[iIndexInAllocationTable] = (uint32_t)(pImage->cbFileCurrent / 512);
1458 pImage->cbFileCurrent += pImage->PCHSGeometry.cSectors * 512;
1459 pImage->fAllocationBitmapChanged = true;
1460 uOffsetInFile = (uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] * 512;
1461
1462 /*
1463 * Write the new block at the current end of the file.
1464 */
1465 rc = parallelsFileWriteUserAsync(pImage, uOffsetInFile, pIoCtx, cbToWrite, NULL, NULL);
1466 if (RT_SUCCESS(rc) || (rc == VERR_VD_ASYNC_IO_IN_PROGRESS))
1467 {
1468 /* Write the changed allocation bitmap entry. */
1469 /** @todo: Error handling. */
1470 rc = parallelsFileWriteMetaAsync(pImage,
1471 sizeof(ParallelsHeader) + iIndexInAllocationTable * sizeof(uint32_t),
1472 &pImage->pAllocationBitmap[iIndexInAllocationTable],
1473 sizeof(uint32_t), pIoCtx,
1474 NULL, NULL);
1475 }
1476
1477 *pcbPreRead = 0;
1478 *pcbPostRead = 0;
1479 }
1480 else
1481 {
1482 uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
1483 rc = parallelsFileWriteUserAsync(pImage, uOffsetInFile, pIoCtx, cbToWrite, NULL, NULL);
1484 }
1485 }
1486
1487 if (pcbWriteProcess)
1488 *pcbWriteProcess = cbToWrite;
1489
1490 LogFlowFunc(("returns %Rrc\n", rc));
1491 return rc;
1492}
1493
1494/** @copydoc VBOXHDDBACKEND::pfnAsyncFlush */
1495static int parallelsAsyncFlush(void *pBackendData, PVDIOCTX pIoCtx)
1496{
1497 int rc = VINF_SUCCESS;
1498 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1499
1500 LogFlowFunc(("pImage=#%p\n", pImage));
1501
1502 /* Flush the file, everything is up to date already. */
1503 rc = parallelsFileFlushAsync(pImage, pIoCtx, NULL, NULL);
1504
1505 LogFlowFunc(("returns %Rrc\n", rc));
1506 return rc;
1507}
1508
1509
1510VBOXHDDBACKEND g_ParallelsBackend =
1511{
1512 /* pszBackendName */
1513 "Parallels",
1514 /* cbSize */
1515 sizeof(VBOXHDDBACKEND),
1516 /* uBackendCaps */
1517 VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS | VD_CAP_CREATE_DYNAMIC | VD_CAP_DIFF,
1518 /* paFileExtensions */
1519 s_aParallelsFileExtensions,
1520 /* paConfigInfo */
1521 NULL,
1522 /* hPlugin */
1523 NIL_RTLDRMOD,
1524 /* pfnCheckIfValid */
1525 parallelsCheckIfValid,
1526 /* pfnOpen */
1527 parallelsOpen,
1528 /* pfnCreate */
1529 parallelsCreate,
1530 /* pfnRename */
1531 parallelsRename,
1532 /* pfnClose */
1533 parallelsClose,
1534 /* pfnRead */
1535 parallelsRead,
1536 /* pfnWrite */
1537 parallelsWrite,
1538 /* pfnFlush */
1539 parallelsFlush,
1540 /* pfnGetVersion */
1541 parallelsGetVersion,
1542 /* pfnGetSize */
1543 parallelsGetSize,
1544 /* pfnGetFileSize */
1545 parallelsGetFileSize,
1546 /* pfnGetPCHSGeometry */
1547 parallelsGetPCHSGeometry,
1548 /* pfnSetPCHSGeometry */
1549 parallelsSetPCHSGeometry,
1550 /* pfnGetLCHSGeometry */
1551 parallelsGetLCHSGeometry,
1552 /* pfnSetLCHSGeometry */
1553 parallelsSetLCHSGeometry,
1554 /* pfnGetImageFlags */
1555 parallelsGetImageFlags,
1556 /* pfnGetOpenFlags */
1557 parallelsGetOpenFlags,
1558 /* pfnSetOpenFlags */
1559 parallelsSetOpenFlags,
1560 /* pfnGetComment */
1561 parallelsGetComment,
1562 /* pfnSetComment */
1563 parallelsSetComment,
1564 /* pfnGetUuid */
1565 parallelsGetUuid,
1566 /* pfnSetUuid */
1567 parallelsSetUuid,
1568 /* pfnGetModificationUuid */
1569 parallelsGetModificationUuid,
1570 /* pfnSetModificationUuid */
1571 parallelsSetModificationUuid,
1572 /* pfnGetParentUuid */
1573 parallelsGetParentUuid,
1574 /* pfnSetParentUuid */
1575 parallelsSetParentUuid,
1576 /* pfnGetParentModificationUuid */
1577 parallelsGetParentModificationUuid,
1578 /* pfnSetParentModificationUuid */
1579 parallelsSetParentModificationUuid,
1580 /* pfnDump */
1581 parallelsDump,
1582 /* pfnGetTimeStamp */
1583 NULL,
1584 /* pfnGetParentTimeStamp */
1585 NULL,
1586 /* pfnSetParentTimeStamp */
1587 NULL,
1588 /* pfnGetParentFilename */
1589 NULL,
1590 /* pfnSetParentFilename */
1591 NULL,
1592 /* pfnAsyncRead */
1593 parallelsAsyncRead,
1594 /* pfnAsyncWrite */
1595 parallelsAsyncWrite,
1596 /* pfnAsyncFlush */
1597 parallelsAsyncFlush,
1598 /* pfnComposeLocation */
1599 genericFileComposeLocation,
1600 /* pfnComposeName */
1601 genericFileComposeName,
1602 /* pfnCompact */
1603 NULL,
1604 /* pfnResize */
1605 NULL
1606};
Note: See TracBrowser for help on using the repository browser.

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