VirtualBox

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

Last change on this file since 96014 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.5 KB
Line 
1/* $Id: Parallels.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 *
4 * Parallels hdd disk image, core code.
5 */
6
7/*
8 * Copyright (C) 2006-2022 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (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#include <iprt/asm.h>
30
31#include "VDBackends.h"
32
33#define PARALLELS_HEADER_MAGIC "WithoutFreeSpace"
34#define PARALLELS_DISK_VERSION 2
35
36/** The header of the parallels disk. */
37#pragma pack(1)
38typedef struct ParallelsHeader
39{
40 /** The magic header to identify a parallels hdd image. */
41 char HeaderIdentifier[16];
42 /** The version of the disk image. */
43 uint32_t uVersion;
44 /** The number of heads the hdd has. */
45 uint32_t cHeads;
46 /** Number of cylinders. */
47 uint32_t cCylinders;
48 /** Number of sectors per track. */
49 uint32_t cSectorsPerTrack;
50 /** Number of entries in the allocation bitmap. */
51 uint32_t cEntriesInAllocationBitmap;
52 /** Total number of sectors. */
53 uint32_t cSectors;
54 /** Padding. */
55 char Padding[24];
56} ParallelsHeader;
57#pragma pack()
58
59/**
60 * Parallels image structure.
61 */
62typedef struct PARALLELSIMAGE
63{
64 /** Image file name. */
65 const char *pszFilename;
66 /** Opaque storage handle. */
67 PVDIOSTORAGE pStorage;
68
69 /** Pointer to the per-disk VD interface list. */
70 PVDINTERFACE pVDIfsDisk;
71 /** Pointer to the per-image VD interface list. */
72 PVDINTERFACE pVDIfsImage;
73 /** Error interface. */
74 PVDINTERFACEERROR pIfError;
75 /** I/O interface. */
76 PVDINTERFACEIOINT pIfIo;
77
78 /** Open flags passed by VBoxHDD layer. */
79 unsigned uOpenFlags;
80 /** Image flags defined during creation or determined during open. */
81 unsigned uImageFlags;
82 /** Total size of the image. */
83 uint64_t cbSize;
84
85 /** Physical geometry of this image. */
86 VDGEOMETRY PCHSGeometry;
87 /** Logical geometry of this image. */
88 VDGEOMETRY LCHSGeometry;
89
90 /** Pointer to the allocation bitmap. */
91 uint32_t *pAllocationBitmap;
92 /** Entries in the allocation bitmap. */
93 uint64_t cAllocationBitmapEntries;
94 /** Flag whether the allocation bitmap was changed. */
95 bool fAllocationBitmapChanged;
96 /** Current file size. */
97 uint64_t cbFileCurrent;
98 /** The static region list. */
99 VDREGIONLIST RegionList;
100} PARALLELSIMAGE, *PPARALLELSIMAGE;
101
102
103/*********************************************************************************************************************************
104* Static Variables *
105*********************************************************************************************************************************/
106
107/** NULL-terminated array of supported file extensions. */
108static const VDFILEEXTENSION s_aParallelsFileExtensions[] =
109{
110 {"hdd", VDTYPE_HDD},
111 {NULL, VDTYPE_INVALID}
112};
113
114/***************************************************
115 * Internal functions *
116 **************************************************/
117
118/**
119 * Internal. Flush image data to disk.
120 */
121static int parallelsFlushImage(PPARALLELSIMAGE pImage)
122{
123 int rc = VINF_SUCCESS;
124
125 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
126 return VINF_SUCCESS;
127
128 if ( !(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
129 && (pImage->fAllocationBitmapChanged))
130 {
131 pImage->fAllocationBitmapChanged = false;
132 /* Write the allocation bitmap to the file. */
133 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage,
134 sizeof(ParallelsHeader), pImage->pAllocationBitmap,
135 pImage->cAllocationBitmapEntries * sizeof(uint32_t));
136 if (RT_FAILURE(rc))
137 return rc;
138 }
139
140 /* Flush file. */
141 rc = vdIfIoIntFileFlushSync(pImage->pIfIo, pImage->pStorage);
142
143 LogFlowFunc(("returns %Rrc\n", rc));
144 return rc;
145}
146
147/**
148 * Internal. Free all allocated space for representing an image except pImage,
149 * and optionally delete the image from disk.
150 */
151static int parallelsFreeImage(PPARALLELSIMAGE pImage, bool fDelete)
152{
153 int rc = VINF_SUCCESS;
154
155 /* Freeing a never allocated image (e.g. because the open failed) is
156 * not signalled as an error. After all nothing bad happens. */
157 if (pImage)
158 {
159 if (pImage->pStorage)
160 {
161 /* No point updating the file that is deleted anyway. */
162 if (!fDelete)
163 parallelsFlushImage(pImage);
164
165 rc = vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage);
166 pImage->pStorage = NULL;
167 }
168
169 if (pImage->pAllocationBitmap)
170 {
171 RTMemFree(pImage->pAllocationBitmap);
172 pImage->pAllocationBitmap = NULL;
173 }
174
175 if (fDelete && pImage->pszFilename)
176 vdIfIoIntFileDelete(pImage->pIfIo, pImage->pszFilename);
177 }
178
179 return rc;
180}
181
182static int parallelsOpenImage(PPARALLELSIMAGE pImage, unsigned uOpenFlags)
183{
184 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
185 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
186 pImage->uOpenFlags = uOpenFlags;
187 AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
188
189 int rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename,
190 VDOpenFlagsToFileOpenFlags(uOpenFlags,
191 false /* fCreate */),
192 &pImage->pStorage);
193 if (RT_SUCCESS(rc))
194 {
195 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &pImage->cbFileCurrent);
196 if (RT_SUCCESS(rc)
197 && !(pImage->cbFileCurrent % 512))
198 {
199 ParallelsHeader parallelsHeader;
200
201 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, 0,
202 &parallelsHeader, sizeof(parallelsHeader));
203 if (RT_SUCCESS(rc))
204 {
205 if (memcmp(parallelsHeader.HeaderIdentifier, PARALLELS_HEADER_MAGIC, 16))
206 {
207 /* Check if the file has hdd as extension. It is a fixed size raw image then. */
208 char *pszSuffix = RTPathSuffix(pImage->pszFilename);
209 if (!strcmp(pszSuffix, ".hdd"))
210 {
211 /* This is a fixed size image. */
212 pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
213 pImage->cbSize = pImage->cbFileCurrent;
214
215 pImage->PCHSGeometry.cHeads = 16;
216 pImage->PCHSGeometry.cSectors = 63;
217 uint64_t cCylinders = pImage->cbSize / (512 * pImage->PCHSGeometry.cSectors * pImage->PCHSGeometry.cHeads);
218 pImage->PCHSGeometry.cCylinders = (uint32_t)cCylinders;
219 }
220 else
221 rc = VERR_VD_PARALLELS_INVALID_HEADER;
222 }
223 else
224 {
225 if ( parallelsHeader.uVersion == PARALLELS_DISK_VERSION
226 && parallelsHeader.cEntriesInAllocationBitmap <= (1 << 30))
227 {
228 Log(("cSectors=%u\n", parallelsHeader.cSectors));
229 pImage->cbSize = ((uint64_t)parallelsHeader.cSectors) * 512;
230 pImage->uImageFlags = VD_IMAGE_FLAGS_NONE;
231 pImage->PCHSGeometry.cCylinders = parallelsHeader.cCylinders;
232 pImage->PCHSGeometry.cHeads = parallelsHeader.cHeads;
233 pImage->PCHSGeometry.cSectors = parallelsHeader.cSectorsPerTrack;
234 pImage->cAllocationBitmapEntries = parallelsHeader.cEntriesInAllocationBitmap;
235 pImage->pAllocationBitmap = (uint32_t *)RTMemAllocZ((uint32_t)pImage->cAllocationBitmapEntries * sizeof(uint32_t));
236 if (RT_LIKELY(pImage->pAllocationBitmap))
237 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage,
238 sizeof(ParallelsHeader), pImage->pAllocationBitmap,
239 pImage->cAllocationBitmapEntries * sizeof(uint32_t));
240 else
241 rc = VERR_NO_MEMORY;
242 }
243 else
244 rc = VERR_NOT_SUPPORTED;
245 }
246 }
247 }
248 else if (RT_SUCCESS(rc))
249 rc = VERR_VD_PARALLELS_INVALID_HEADER;
250 }
251
252 if (RT_SUCCESS(rc))
253 {
254 PVDREGIONDESC pRegion = &pImage->RegionList.aRegions[0];
255 pImage->RegionList.fFlags = 0;
256 pImage->RegionList.cRegions = 1;
257
258 pRegion->offRegion = 0; /* Disk start. */
259 pRegion->cbBlock = 512;
260 pRegion->enmDataForm = VDREGIONDATAFORM_RAW;
261 pRegion->enmMetadataForm = VDREGIONMETADATAFORM_NONE;
262 pRegion->cbData = 512;
263 pRegion->cbMetadata = 0;
264 pRegion->cRegionBlocksOrBytes = pImage->cbSize;
265 }
266 else
267 parallelsFreeImage(pImage, false);
268
269 LogFlowFunc(("returns %Rrc\n", rc));
270 return rc;
271}
272
273/**
274 * Internal: Create a parallels image.
275 */
276static int parallelsCreateImage(PPARALLELSIMAGE pImage, uint64_t cbSize,
277 unsigned uImageFlags, const char *pszComment,
278 PCVDGEOMETRY pPCHSGeometry,
279 PCVDGEOMETRY pLCHSGeometry, unsigned uOpenFlags,
280 PFNVDPROGRESS pfnProgress, void *pvUser,
281 unsigned uPercentStart, unsigned uPercentSpan)
282{
283 RT_NOREF1(pszComment);
284 int rc = VINF_SUCCESS;
285 int32_t fOpen;
286
287 if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
288 {
289 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
290 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
291 AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
292
293 pImage->uOpenFlags = uOpenFlags & ~VD_OPEN_FLAGS_READONLY;
294 pImage->uImageFlags = uImageFlags;
295 pImage->PCHSGeometry = *pPCHSGeometry;
296 pImage->LCHSGeometry = *pLCHSGeometry;
297 if (!pImage->PCHSGeometry.cCylinders)
298 {
299 /* Set defaults. */
300 pImage->PCHSGeometry.cSectors = 63;
301 pImage->PCHSGeometry.cHeads = 16;
302 pImage->PCHSGeometry.cCylinders = pImage->cbSize / (512 * pImage->PCHSGeometry.cSectors * pImage->PCHSGeometry.cHeads);
303 }
304
305 /* Create image file. */
306 fOpen = VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags, true /* fCreate */);
307 rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename, fOpen, &pImage->pStorage);
308 if (RT_SUCCESS(rc))
309 {
310 if (pfnProgress)
311 pfnProgress(pvUser, uPercentStart + uPercentSpan * 98 / 100);
312
313 /* Setup image state. */
314 pImage->cbSize = cbSize;
315 pImage->cAllocationBitmapEntries = cbSize / 512 / pImage->PCHSGeometry.cSectors;
316 if (pImage->cAllocationBitmapEntries * pImage->PCHSGeometry.cSectors * 512 < cbSize)
317 pImage->cAllocationBitmapEntries++;
318 pImage->fAllocationBitmapChanged = true;
319 pImage->cbFileCurrent = sizeof(ParallelsHeader) + pImage->cAllocationBitmapEntries * sizeof(uint32_t);
320 /* Round to next sector boundary. */
321 pImage->cbFileCurrent += 512 - pImage->cbFileCurrent % 512;
322 Assert(!(pImage->cbFileCurrent % 512));
323 pImage->pAllocationBitmap = (uint32_t *)RTMemAllocZ(pImage->cAllocationBitmapEntries * sizeof(uint32_t));
324 if (pImage->pAllocationBitmap)
325 {
326 ParallelsHeader Header;
327
328 memcpy(Header.HeaderIdentifier, PARALLELS_HEADER_MAGIC, sizeof(Header.HeaderIdentifier));
329 Header.uVersion = RT_H2LE_U32(PARALLELS_DISK_VERSION);
330 Header.cHeads = RT_H2LE_U32(pImage->PCHSGeometry.cHeads);
331 Header.cCylinders = RT_H2LE_U32(pImage->PCHSGeometry.cCylinders);
332 Header.cSectorsPerTrack = RT_H2LE_U32(pImage->PCHSGeometry.cSectors);
333 Header.cEntriesInAllocationBitmap = RT_H2LE_U32(pImage->cAllocationBitmapEntries);
334 Header.cSectors = RT_H2LE_U32(pImage->cbSize / 512);
335 memset(Header.Padding, 0, sizeof(Header.Padding));
336
337 /* Write header and allocation bitmap. */
338 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, pImage->cbFileCurrent);
339 if (RT_SUCCESS(rc))
340 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0,
341 &Header, sizeof(Header));
342 if (RT_SUCCESS(rc))
343 rc = parallelsFlushImage(pImage); /* Writes the allocation bitmap. */
344 }
345 else
346 rc = VERR_NO_MEMORY;
347 }
348 else
349 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("Parallels: cannot create image '%s'"), pImage->pszFilename);
350 }
351 else
352 rc = vdIfError(pImage->pIfError, VERR_VD_INVALID_TYPE, RT_SRC_POS, N_("Parallels: cannot create fixed image '%s'. Create a raw image"), pImage->pszFilename);
353
354 if (RT_SUCCESS(rc) && pfnProgress)
355 pfnProgress(pvUser, uPercentStart + uPercentSpan);
356
357 if (RT_SUCCESS(rc))
358 {
359 PVDREGIONDESC pRegion = &pImage->RegionList.aRegions[0];
360 pImage->RegionList.fFlags = 0;
361 pImage->RegionList.cRegions = 1;
362
363 pRegion->offRegion = 0; /* Disk start. */
364 pRegion->cbBlock = 512;
365 pRegion->enmDataForm = VDREGIONDATAFORM_RAW;
366 pRegion->enmMetadataForm = VDREGIONMETADATAFORM_NONE;
367 pRegion->cbData = 512;
368 pRegion->cbMetadata = 0;
369 pRegion->cRegionBlocksOrBytes = pImage->cbSize;
370 }
371 else
372 parallelsFreeImage(pImage, rc != VERR_ALREADY_EXISTS);
373 return rc;
374}
375
376/** @copydoc VDIMAGEBACKEND::pfnProbe */
377static DECLCALLBACK(int) parallelsProbe(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
378 PVDINTERFACE pVDIfsImage, VDTYPE enmDesiredType, VDTYPE *penmType)
379{
380 RT_NOREF(pVDIfsDisk, enmDesiredType);
381 int rc;
382 PVDIOSTORAGE pStorage;
383 ParallelsHeader parallelsHeader;
384
385 PVDINTERFACEIOINT pIfIo = VDIfIoIntGet(pVDIfsImage);
386 AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
387
388 rc = vdIfIoIntFileOpen(pIfIo, pszFilename,
389 VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_READONLY,
390 false /* fCreate */),
391 &pStorage);
392 if (RT_FAILURE(rc))
393 return rc;
394
395 rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0, &parallelsHeader,
396 sizeof(ParallelsHeader));
397 if (RT_SUCCESS(rc))
398 {
399 if ( !memcmp(parallelsHeader.HeaderIdentifier, PARALLELS_HEADER_MAGIC, 16)
400 && (parallelsHeader.uVersion == PARALLELS_DISK_VERSION))
401 rc = VINF_SUCCESS;
402 else
403 {
404 /*
405 * The image may be an fixed size image.
406 * Unfortunately fixed sized parallels images
407 * are just raw files hence no magic header to
408 * check for.
409 * The code succeeds if the file is a multiple
410 * of 512 and if the file extensions is *.hdd
411 */
412 uint64_t cbFile;
413 char *pszSuffix;
414
415 rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
416 if (RT_FAILURE(rc) || ((cbFile % 512) != 0))
417 {
418 vdIfIoIntFileClose(pIfIo, pStorage);
419 return VERR_VD_PARALLELS_INVALID_HEADER;
420 }
421
422 pszSuffix = RTPathSuffix(pszFilename);
423 if (!pszSuffix || strcmp(pszSuffix, ".hdd"))
424 rc = VERR_VD_PARALLELS_INVALID_HEADER;
425 else
426 rc = VINF_SUCCESS;
427 }
428 }
429
430 if (RT_SUCCESS(rc))
431 *penmType = VDTYPE_HDD;
432
433 vdIfIoIntFileClose(pIfIo, pStorage);
434 return rc;
435}
436
437/** @copydoc VDIMAGEBACKEND::pfnOpen */
438static DECLCALLBACK(int) parallelsOpen(const char *pszFilename, unsigned uOpenFlags,
439 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
440 VDTYPE enmType, void **ppBackendData)
441{
442 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n", pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
443 int rc;
444 PPARALLELSIMAGE pImage;
445
446 NOREF(enmType); /**< @todo r=klaus make use of the type info. */
447
448 /* Check parameters. */
449 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
450 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
451 AssertReturn(*pszFilename != '\0', VERR_INVALID_PARAMETER);
452
453
454 pImage = (PPARALLELSIMAGE)RTMemAllocZ(RT_UOFFSETOF(PARALLELSIMAGE, RegionList.aRegions[1]));
455 if (RT_LIKELY(pImage))
456 {
457 pImage->pszFilename = pszFilename;
458 pImage->pStorage = NULL;
459 pImage->pVDIfsDisk = pVDIfsDisk;
460 pImage->pVDIfsImage = pVDIfsImage;
461 pImage->fAllocationBitmapChanged = false;
462
463 rc = parallelsOpenImage(pImage, uOpenFlags);
464 if (RT_SUCCESS(rc))
465 *ppBackendData = pImage;
466 else
467 RTMemFree(pImage);
468 }
469 else
470 rc = VERR_NO_MEMORY;
471
472 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
473 return rc;
474}
475
476/** @copydoc VDIMAGEBACKEND::pfnCreate */
477static DECLCALLBACK(int) parallelsCreate(const char *pszFilename, uint64_t cbSize,
478 unsigned uImageFlags, const char *pszComment,
479 PCVDGEOMETRY pPCHSGeometry,
480 PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
481 unsigned uOpenFlags, unsigned uPercentStart,
482 unsigned uPercentSpan, PVDINTERFACE pVDIfsDisk,
483 PVDINTERFACE pVDIfsImage,
484 PVDINTERFACE pVDIfsOperation, VDTYPE enmType,
485 void **ppBackendData)
486{
487 RT_NOREF1(pUuid);
488 LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p enmType=%u ppBackendData=%#p",
489 pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData));
490
491 /* Check the VD container type. */
492 if (enmType != VDTYPE_HDD)
493 return VERR_VD_INVALID_TYPE;
494
495 /* Check arguments. */
496 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
497 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
498 AssertReturn(*pszFilename != '\0', VERR_INVALID_PARAMETER);
499 AssertPtrReturn(pPCHSGeometry, VERR_INVALID_POINTER);
500 AssertPtrReturn(pLCHSGeometry, VERR_INVALID_POINTER);
501
502 int rc = VINF_SUCCESS;
503 PPARALLELSIMAGE pImage;
504 PFNVDPROGRESS pfnProgress = NULL;
505 void *pvUser = NULL;
506 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
507 if (pIfProgress)
508 {
509 pfnProgress = pIfProgress->pfnProgress;
510 pvUser = pIfProgress->Core.pvUser;
511 }
512
513 pImage = (PPARALLELSIMAGE)RTMemAllocZ(RT_UOFFSETOF(PARALLELSIMAGE, RegionList.aRegions[1]));
514 if (RT_LIKELY(pImage))
515 {
516 pImage->pszFilename = pszFilename;
517 pImage->pStorage = NULL;
518 pImage->pVDIfsDisk = pVDIfsDisk;
519 pImage->pVDIfsImage = pVDIfsImage;
520
521 rc = parallelsCreateImage(pImage, cbSize, uImageFlags, pszComment,
522 pPCHSGeometry, pLCHSGeometry, uOpenFlags,
523 pfnProgress, pvUser, uPercentStart, uPercentSpan);
524 if (RT_SUCCESS(rc))
525 {
526 /* So far the image is opened in read/write mode. Make sure the
527 * image is opened in read-only mode if the caller requested that. */
528 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
529 {
530 parallelsFreeImage(pImage, false);
531 rc = parallelsOpenImage(pImage, uOpenFlags);
532 }
533
534 if (RT_SUCCESS(rc))
535 *ppBackendData = pImage;
536 }
537
538 if (RT_FAILURE(rc))
539 RTMemFree(pImage);
540 }
541 else
542 rc = VERR_NO_MEMORY;
543
544 LogFlowFunc(("returns %Rrc\n", rc));
545 return rc;
546}
547
548/** @copydoc VDIMAGEBACKEND::pfnRename */
549static DECLCALLBACK(int) parallelsRename(void *pBackendData, const char *pszFilename)
550{
551 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
552 int rc = VINF_SUCCESS;
553 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
554
555 /* Check arguments. */
556 AssertReturn((pImage && pszFilename && *pszFilename), VERR_INVALID_PARAMETER);
557
558 /* Close the image. */
559 rc = parallelsFreeImage(pImage, false);
560 if (RT_SUCCESS(rc))
561 {
562 /* Rename the file. */
563 rc = vdIfIoIntFileMove(pImage->pIfIo, pImage->pszFilename, pszFilename, 0);
564 if (RT_SUCCESS(rc))
565 {
566 /* Update pImage with the new information. */
567 pImage->pszFilename = pszFilename;
568
569 /* Open the old image with new name. */
570 rc = parallelsOpenImage(pImage, pImage->uOpenFlags);
571 }
572 else
573 {
574 /* The move failed, try to reopen the original image. */
575 int rc2 = parallelsOpenImage(pImage, pImage->uOpenFlags);
576 if (RT_FAILURE(rc2))
577 rc = rc2;
578 }
579 }
580
581 LogFlowFunc(("returns %Rrc\n", rc));
582 return rc;
583}
584
585/** @copydoc VDIMAGEBACKEND::pfnClose */
586static DECLCALLBACK(int) parallelsClose(void *pBackendData, bool fDelete)
587{
588 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
589 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
590 int rc = parallelsFreeImage(pImage, fDelete);
591 RTMemFree(pImage);
592
593 LogFlowFunc(("returns %Rrc\n", rc));
594 return rc;
595}
596
597/** @copydoc VDIMAGEBACKEND::pfnRead */
598static DECLCALLBACK(int) parallelsRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
599 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
600{
601 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
602 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
603 int rc = VINF_SUCCESS;
604 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
605 uint64_t uSector;
606 uint64_t uOffsetInFile;
607 uint32_t iIndexInAllocationTable;
608
609 AssertPtr(pImage);
610 Assert(uOffset % 512 == 0);
611 Assert(cbToRead % 512 == 0);
612
613 if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
614 rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, uOffset,
615 pIoCtx, cbToRead);
616 else
617 {
618 /* Calculate offset in the real file. */
619 uSector = uOffset / 512;
620 /* One chunk in the file is always one track big. */
621 iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
622 uSector = uSector % pImage->PCHSGeometry.cSectors;
623
624 cbToRead = RT_MIN(cbToRead, (pImage->PCHSGeometry.cSectors - uSector)*512);
625
626 if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
627 rc = VERR_VD_BLOCK_FREE;
628 else
629 {
630 uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
631 rc = vdIfIoIntFileReadUser(pImage->pIfIo, pImage->pStorage, uOffsetInFile,
632 pIoCtx, cbToRead);
633 }
634 }
635
636 *pcbActuallyRead = cbToRead;
637
638 LogFlowFunc(("returns %Rrc\n", rc));
639 return rc;
640}
641
642/** @copydoc VDIMAGEBACKEND::pfnWrite */
643static DECLCALLBACK(int) parallelsWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
644 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
645 size_t *pcbPostRead, unsigned fWrite)
646{
647 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p\n",
648 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess));
649 int rc = VINF_SUCCESS;
650 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
651 uint64_t uSector;
652 uint64_t uOffsetInFile;
653 uint32_t iIndexInAllocationTable;
654
655 AssertPtr(pImage);
656 Assert(uOffset % 512 == 0);
657 Assert(cbToWrite % 512 == 0);
658
659 if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
660 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage, uOffset,
661 pIoCtx, cbToWrite, NULL, NULL);
662 else
663 {
664 /* Calculate offset in the real file. */
665 uSector = uOffset / 512;
666 /* One chunk in the file is always one track big. */
667 iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors);
668 uSector = uSector % pImage->PCHSGeometry.cSectors;
669
670 cbToWrite = RT_MIN(cbToWrite, (pImage->PCHSGeometry.cSectors - uSector)*512);
671
672 if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0)
673 {
674 if (fWrite & VD_WRITE_NO_ALLOC)
675 {
676 *pcbPreRead = uSector * 512;
677 *pcbPostRead = pImage->PCHSGeometry.cSectors * 512 - cbToWrite - *pcbPreRead;
678
679 if (pcbWriteProcess)
680 *pcbWriteProcess = cbToWrite;
681 return VERR_VD_BLOCK_FREE;
682 }
683
684 /* Allocate new chunk in the file. */
685 Assert(uSector == 0);
686 AssertMsg(pImage->cbFileCurrent % 512 == 0, ("File size is not a multiple of 512\n"));
687 pImage->pAllocationBitmap[iIndexInAllocationTable] = (uint32_t)(pImage->cbFileCurrent / 512);
688 pImage->cbFileCurrent += pImage->PCHSGeometry.cSectors * 512;
689 pImage->fAllocationBitmapChanged = true;
690 uOffsetInFile = (uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] * 512;
691
692 /*
693 * Write the new block at the current end of the file.
694 */
695 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
696 uOffsetInFile, pIoCtx, cbToWrite, NULL, NULL);
697 if (RT_SUCCESS(rc) || (rc == VERR_VD_ASYNC_IO_IN_PROGRESS))
698 {
699 /* Write the changed allocation bitmap entry. */
700 /** @todo Error handling. */
701 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pImage->pStorage,
702 sizeof(ParallelsHeader) + iIndexInAllocationTable * sizeof(uint32_t),
703 &pImage->pAllocationBitmap[iIndexInAllocationTable],
704 sizeof(uint32_t), pIoCtx,
705 NULL, NULL);
706 }
707
708 *pcbPreRead = 0;
709 *pcbPostRead = 0;
710 }
711 else
712 {
713 uOffsetInFile = (pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512;
714 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pImage->pStorage,
715 uOffsetInFile, pIoCtx, cbToWrite, NULL, NULL);
716 }
717 }
718
719 if (pcbWriteProcess)
720 *pcbWriteProcess = cbToWrite;
721
722 LogFlowFunc(("returns %Rrc\n", rc));
723 return rc;
724}
725
726/** @copydoc VDIMAGEBACKEND::pfnFlush */
727static DECLCALLBACK(int) parallelsFlush(void *pBackendData, PVDIOCTX pIoCtx)
728{
729 int rc = VINF_SUCCESS;
730 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
731
732 LogFlowFunc(("pImage=#%p\n", pImage));
733
734 /* Flush the file, everything is up to date already. */
735 rc = vdIfIoIntFileFlush(pImage->pIfIo, pImage->pStorage, pIoCtx, NULL, NULL);
736
737 LogFlowFunc(("returns %Rrc\n", rc));
738 return rc;
739}
740
741/** @copydoc VDIMAGEBACKEND::pfnGetVersion */
742static DECLCALLBACK(unsigned) parallelsGetVersion(void *pBackendData)
743{
744 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
745 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
746
747 AssertPtrReturn(pImage, 0);
748
749 return PARALLELS_DISK_VERSION;
750}
751
752/** @copydoc VDIMAGEBACKEND::pfnGetFileSize */
753static DECLCALLBACK(uint64_t) parallelsGetFileSize(void *pBackendData)
754{
755 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
756 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
757 uint64_t cb = 0;
758
759 AssertPtrReturn(pImage, 0);
760
761 if (pImage->pStorage)
762 cb = pImage->cbFileCurrent;
763
764 LogFlowFunc(("returns %lld\n", cb));
765 return cb;
766}
767
768/** @copydoc VDIMAGEBACKEND::pfnGetPCHSGeometry */
769static DECLCALLBACK(int) parallelsGetPCHSGeometry(void *pBackendData,
770 PVDGEOMETRY pPCHSGeometry)
771{
772 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
773 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
774 int rc = VINF_SUCCESS;
775
776 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
777
778 if (pImage->PCHSGeometry.cCylinders)
779 *pPCHSGeometry = pImage->PCHSGeometry;
780 else
781 rc = VERR_VD_GEOMETRY_NOT_SET;
782
783 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
784 return rc;
785}
786
787/** @copydoc VDIMAGEBACKEND::pfnSetPCHSGeometry */
788static DECLCALLBACK(int) parallelsSetPCHSGeometry(void *pBackendData,
789 PCVDGEOMETRY pPCHSGeometry)
790{
791 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pBackendData,
792 pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
793 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
794 int rc = VINF_SUCCESS;
795
796 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
797
798 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
799 rc = VERR_VD_IMAGE_READ_ONLY;
800 else
801 pImage->PCHSGeometry = *pPCHSGeometry;
802
803 LogFlowFunc(("returns %Rrc\n", rc));
804 return rc;
805}
806
807/** @copydoc VDIMAGEBACKEND::pfnGetLCHSGeometry */
808static DECLCALLBACK(int) parallelsGetLCHSGeometry(void *pBackendData,
809 PVDGEOMETRY pLCHSGeometry)
810{
811 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
812 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
813 int rc = VINF_SUCCESS;
814
815 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
816
817 if (pImage->LCHSGeometry.cCylinders)
818 *pLCHSGeometry = pImage->LCHSGeometry;
819 else
820 rc = VERR_VD_GEOMETRY_NOT_SET;
821
822 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
823 return rc;
824}
825
826/** @copydoc VDIMAGEBACKEND::pfnSetLCHSGeometry */
827static DECLCALLBACK(int) parallelsSetLCHSGeometry(void *pBackendData,
828 PCVDGEOMETRY pLCHSGeometry)
829{
830 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
831 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
832 int rc = VINF_SUCCESS;
833
834 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
835
836 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
837 rc = VERR_VD_IMAGE_READ_ONLY;
838 else
839 pImage->LCHSGeometry = *pLCHSGeometry;
840
841 LogFlowFunc(("returns %Rrc\n", rc));
842 return rc;
843}
844
845/** @copydoc VDIMAGEBACKEND::pfnQueryRegions */
846static DECLCALLBACK(int) parallelsQueryRegions(void *pBackendData, PCVDREGIONLIST *ppRegionList)
847{
848 LogFlowFunc(("pBackendData=%#p ppRegionList=%#p\n", pBackendData, ppRegionList));
849 PPARALLELSIMAGE pThis = (PPARALLELSIMAGE)pBackendData;
850
851 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
852
853 *ppRegionList = &pThis->RegionList;
854 LogFlowFunc(("returns %Rrc\n", VINF_SUCCESS));
855 return VINF_SUCCESS;
856}
857
858/** @copydoc VDIMAGEBACKEND::pfnRegionListRelease */
859static DECLCALLBACK(void) parallelsRegionListRelease(void *pBackendData, PCVDREGIONLIST pRegionList)
860{
861 RT_NOREF1(pRegionList);
862 LogFlowFunc(("pBackendData=%#p pRegionList=%#p\n", pBackendData, pRegionList));
863 PPARALLELSIMAGE pThis = (PPARALLELSIMAGE)pBackendData;
864 AssertPtr(pThis); RT_NOREF(pThis);
865
866 /* Nothing to do here. */
867}
868
869/** @copydoc VDIMAGEBACKEND::pfnGetImageFlags */
870static DECLCALLBACK(unsigned) parallelsGetImageFlags(void *pBackendData)
871{
872 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
873 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
874
875 AssertPtrReturn(pImage, 0);
876
877 LogFlowFunc(("returns %#x\n", pImage->uImageFlags));
878 return pImage->uImageFlags;
879}
880
881/** @copydoc VDIMAGEBACKEND::pfnGetOpenFlags */
882static DECLCALLBACK(unsigned) parallelsGetOpenFlags(void *pBackendData)
883{
884 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
885 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
886
887 AssertPtrReturn(pImage, 0);
888
889 LogFlowFunc(("returns %#x\n", pImage->uOpenFlags));
890 return pImage->uOpenFlags;
891}
892
893/** @copydoc VDIMAGEBACKEND::pfnSetOpenFlags */
894static DECLCALLBACK(int) parallelsSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
895{
896 LogFlowFunc(("pBackendData=%#p\n uOpenFlags=%#x", pBackendData, uOpenFlags));
897 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
898 int rc = VINF_SUCCESS;
899
900 /* Image must be opened and the new flags must be valid. */
901 if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
902 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
903 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
904 rc = VERR_INVALID_PARAMETER;
905 else
906 {
907 /* Implement this operation via reopening the image. */
908 parallelsFreeImage(pImage, false);
909 rc = parallelsOpenImage(pImage, uOpenFlags);
910 }
911
912 LogFlowFunc(("returns %Rrc\n", rc));
913 return rc;
914}
915
916/** @copydoc VDIMAGEBACKEND::pfnGetComment */
917static DECLCALLBACK(int) parallelsGetComment(void *pBackendData, char *pszComment,
918 size_t cbComment)
919{
920 RT_NOREF2(pszComment, cbComment);
921 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
922 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
923
924 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
925
926 LogFlowFunc(("returns %Rrc comment='%s'\n", VERR_NOT_SUPPORTED, pszComment));
927 return VERR_NOT_SUPPORTED;
928}
929
930/** @copydoc VDIMAGEBACKEND::pfnSetComment */
931static DECLCALLBACK(int) parallelsSetComment(void *pBackendData, const char *pszComment)
932{
933 RT_NOREF1(pszComment);
934 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
935 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
936
937 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
938
939 int rc;
940 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
941 rc = VERR_VD_IMAGE_READ_ONLY;
942 else
943 rc = VERR_NOT_SUPPORTED;
944
945 LogFlowFunc(("returns %Rrc\n", rc));
946 return rc;
947}
948
949/** @copydoc VDIMAGEBACKEND::pfnGetUuid */
950static DECLCALLBACK(int) parallelsGetUuid(void *pBackendData, PRTUUID pUuid)
951{
952 RT_NOREF1(pUuid);
953 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
954 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
955
956 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
957
958 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VERR_NOT_SUPPORTED, pUuid));
959 return VERR_NOT_SUPPORTED;
960}
961
962/** @copydoc VDIMAGEBACKEND::pfnSetUuid */
963static DECLCALLBACK(int) parallelsSetUuid(void *pBackendData, PCRTUUID pUuid)
964{
965 RT_NOREF1(pUuid);
966 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
967 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
968
969 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
970
971 int rc;
972 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
973 rc = VERR_VD_IMAGE_READ_ONLY;
974 else
975 rc = VERR_NOT_SUPPORTED;
976
977 LogFlowFunc(("returns %Rrc\n", rc));
978 return rc;
979}
980
981/** @copydoc VDIMAGEBACKEND::pfnGetModificationUuid */
982static DECLCALLBACK(int) parallelsGetModificationUuid(void *pBackendData, PRTUUID pUuid)
983{
984 RT_NOREF1(pUuid);
985 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
986 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
987
988 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
989
990 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VERR_NOT_SUPPORTED, pUuid));
991 return VERR_NOT_SUPPORTED;
992}
993
994/** @copydoc VDIMAGEBACKEND::pfnSetModificationUuid */
995static DECLCALLBACK(int) parallelsSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
996{
997 RT_NOREF1(pUuid);
998 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
999 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1000
1001 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
1002
1003 int rc;
1004 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1005 rc = VERR_VD_IMAGE_READ_ONLY;
1006 else
1007 rc = VERR_NOT_SUPPORTED;
1008
1009 LogFlowFunc(("returns %Rrc\n", rc));
1010 return rc;
1011}
1012
1013/** @copydoc VDIMAGEBACKEND::pfnGetParentUuid */
1014static DECLCALLBACK(int) parallelsGetParentUuid(void *pBackendData, PRTUUID pUuid)
1015{
1016 RT_NOREF1(pUuid);
1017 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1018 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1019
1020 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
1021
1022 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VERR_NOT_SUPPORTED, pUuid));
1023 return VERR_NOT_SUPPORTED;
1024}
1025
1026/** @copydoc VDIMAGEBACKEND::pfnSetParentUuid */
1027static DECLCALLBACK(int) parallelsSetParentUuid(void *pBackendData, PCRTUUID pUuid)
1028{
1029 RT_NOREF1(pUuid);
1030 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1031 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1032
1033 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
1034
1035 int rc;
1036 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1037 rc = VERR_VD_IMAGE_READ_ONLY;
1038 else
1039 rc = VERR_NOT_SUPPORTED;
1040
1041 LogFlowFunc(("returns %Rrc\n", rc));
1042 return rc;
1043}
1044
1045/** @copydoc VDIMAGEBACKEND::pfnGetParentModificationUuid */
1046static DECLCALLBACK(int) parallelsGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
1047{
1048 RT_NOREF1(pUuid);
1049 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
1050 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1051
1052 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
1053
1054 int rc;
1055 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1056 rc = VERR_VD_IMAGE_READ_ONLY;
1057 else
1058 rc = VERR_NOT_SUPPORTED;
1059
1060 LogFlowFunc(("returns %Rrc (%RTuuid)\n", rc, pUuid));
1061 return rc;
1062}
1063
1064/** @copydoc VDIMAGEBACKEND::pfnSetParentModificationUuid */
1065static DECLCALLBACK(int) parallelsSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
1066{
1067 RT_NOREF1(pUuid);
1068 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
1069 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1070
1071 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
1072
1073 int rc;
1074 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
1075 rc = VERR_VD_IMAGE_READ_ONLY;
1076 else
1077 rc = VERR_NOT_SUPPORTED;
1078
1079 LogFlowFunc(("returns %Rrc\n", rc));
1080 return rc;
1081}
1082
1083/** @copydoc VDIMAGEBACKEND::pfnDump */
1084static DECLCALLBACK(void) parallelsDump(void *pBackendData)
1085{
1086 PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData;
1087
1088 AssertPtrReturnVoid(pImage);
1089 vdIfErrorMessage(pImage->pIfError, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u\n",
1090 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
1091 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors);
1092}
1093
1094
1095
1096const VDIMAGEBACKEND g_ParallelsBackend =
1097{
1098 /* u32Version */
1099 VD_IMGBACKEND_VERSION,
1100 /* pszBackendName */
1101 "Parallels",
1102 /* uBackendCaps */
1103 VD_CAP_FILE | VD_CAP_ASYNC | VD_CAP_VFS | VD_CAP_CREATE_DYNAMIC | VD_CAP_DIFF,
1104 /* paFileExtensions */
1105 s_aParallelsFileExtensions,
1106 /* paConfigInfo */
1107 NULL,
1108 /* pfnProbe */
1109 parallelsProbe,
1110 /* pfnOpen */
1111 parallelsOpen,
1112 /* pfnCreate */
1113 parallelsCreate,
1114 /* pfnRename */
1115 parallelsRename,
1116 /* pfnClose */
1117 parallelsClose,
1118 /* pfnRead */
1119 parallelsRead,
1120 /* pfnWrite */
1121 parallelsWrite,
1122 /* pfnFlush */
1123 parallelsFlush,
1124 /* pfnDiscard */
1125 NULL,
1126 /* pfnGetVersion */
1127 parallelsGetVersion,
1128 /* pfnGetFileSize */
1129 parallelsGetFileSize,
1130 /* pfnGetPCHSGeometry */
1131 parallelsGetPCHSGeometry,
1132 /* pfnSetPCHSGeometry */
1133 parallelsSetPCHSGeometry,
1134 /* pfnGetLCHSGeometry */
1135 parallelsGetLCHSGeometry,
1136 /* pfnSetLCHSGeometry */
1137 parallelsSetLCHSGeometry,
1138 /* pfnQueryRegions */
1139 parallelsQueryRegions,
1140 /* pfnRegionListRelease */
1141 parallelsRegionListRelease,
1142 /* pfnGetImageFlags */
1143 parallelsGetImageFlags,
1144 /* pfnGetOpenFlags */
1145 parallelsGetOpenFlags,
1146 /* pfnSetOpenFlags */
1147 parallelsSetOpenFlags,
1148 /* pfnGetComment */
1149 parallelsGetComment,
1150 /* pfnSetComment */
1151 parallelsSetComment,
1152 /* pfnGetUuid */
1153 parallelsGetUuid,
1154 /* pfnSetUuid */
1155 parallelsSetUuid,
1156 /* pfnGetModificationUuid */
1157 parallelsGetModificationUuid,
1158 /* pfnSetModificationUuid */
1159 parallelsSetModificationUuid,
1160 /* pfnGetParentUuid */
1161 parallelsGetParentUuid,
1162 /* pfnSetParentUuid */
1163 parallelsSetParentUuid,
1164 /* pfnGetParentModificationUuid */
1165 parallelsGetParentModificationUuid,
1166 /* pfnSetParentModificationUuid */
1167 parallelsSetParentModificationUuid,
1168 /* pfnDump */
1169 parallelsDump,
1170 /* pfnGetTimestamp */
1171 NULL,
1172 /* pfnGetParentTimestamp */
1173 NULL,
1174 /* pfnSetParentTimestamp */
1175 NULL,
1176 /* pfnGetParentFilename */
1177 NULL,
1178 /* pfnSetParentFilename */
1179 NULL,
1180 /* pfnComposeLocation */
1181 genericFileComposeLocation,
1182 /* pfnComposeName */
1183 genericFileComposeName,
1184 /* pfnCompact */
1185 NULL,
1186 /* pfnResize */
1187 NULL,
1188 /* pfnRepair */
1189 NULL,
1190 /* pfnTraverseMetadata */
1191 NULL,
1192 /* u32VersionEnd */
1193 VD_IMGBACKEND_VERSION
1194};
Note: See TracBrowser for help on using the repository browser.

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