VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/VBoxHDD-new.cpp@ 2392

Last change on this file since 2392 was 2379, checked in by vboxsync, 18 years ago

export to OSE

File size: 50.0 KB
Line 
1/** $Id$ */
2/** @file
3 * VBox HDD Container implementation.
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_VD
26#include <VBox/VBoxHDD-new.h>
27#include <VBox/err.h>
28
29#include <VBox/log.h>
30#include <iprt/alloc.h>
31#include <iprt/assert.h>
32#include <iprt/uuid.h>
33#include <iprt/file.h>
34#include <iprt/string.h>
35#include <iprt/asm.h>
36
37#include "VBoxHDD-newInternal.h"
38
39
40#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
41
42
43/**
44 * VBox HDD Container image descriptor.
45 */
46typedef struct VBOXHDDIMAGEDESC
47{
48 /** Link to parent image descriptor, if any. */
49 struct VBOXHDDIMAGEDESC *pPrev;
50 /** Link to child image descriptor, if any. */
51 struct VBOXHDDIMAGEDESC *pNext;
52 /** Container base filename. (UTF-8) */
53 char *pszFilename;
54 /** Data managed by the backend which keeps the actual info. */
55 void *pvBackendData;
56} VBOXHDDIMAGEDESC, *PVBOXHDDIMAGEDESC;
57
58/**
59 * uModified bit flags.
60 */
61#define VD_IMAGE_MODIFIED_FLAG BIT(0)
62#define VD_IMAGE_MODIFIED_FIRST BIT(1)
63#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE BIT(2)
64
65
66/**
67 * VBox HDD Container main structure, private part.
68 */
69struct VBOXHDD
70{
71 /** Structure signature (VBOXHDDDISK_SIGNATURE). */
72 uint32_t u32Signature;
73
74 /** Number of opened images. */
75 unsigned cImages;
76
77 /** Base image. */
78 PVBOXHDDIMAGEDESC pBase;
79
80 /** Last opened image in the chain.
81 * The same as pBase if only one image is used or the last opened diff image. */
82 PVBOXHDDIMAGEDESC pLast;
83
84 /** Flags representing the modification state. */
85 unsigned uModified;
86
87 /** Cached size of this disk. */
88 uint64_t cbSize;
89 /** Cached CHS geometry for this disk, cylinders. */
90 unsigned cCylinders;
91 /** Cached CHS geometry for this disk, heads. */
92 unsigned cHeads;
93 /** Cached CHS geometry for this disk, sectors. */
94 unsigned cSectors;
95 /** Cached translation mode for this disk. */
96 PDMBIOSTRANSLATION enmTranslation;
97
98 /** Error message processing callback. */
99 PFNVDERROR pfnError;
100 /** Opaque data for error callback. */
101 void *pvErrorUser;
102
103 /** Function pointers for the various backend methods. */
104 PVBOXHDDBACKEND Backend;
105};
106
107
108typedef struct
109{
110 const char *pszBackendName;
111 PVBOXHDDBACKEND Backend;
112} VBOXHDDBACKENDENTRY;
113
114
115extern VBOXHDDBACKEND g_VmdkBackend;
116
117
118static const VBOXHDDBACKENDENTRY aBackends[] =
119{
120 { "VMDK", &g_VmdkBackend },
121 { NULL, NULL }
122};
123
124
125/**
126 * internal: issue early error message.
127 */
128static int vdEarlyError(PFNVDERROR pfnError, void *pvErrorUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, ...)
129{
130 va_list va;
131 va_start(va, pszFormat);
132 pfnError(pvErrorUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
133 va_end(va);
134 return rc;
135}
136
137/**
138 * internal: issue error message.
139 */
140static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL, const char *pszFormat, ...)
141{
142 va_list va;
143 va_start(va, pszFormat);
144 pDisk->pfnError(pDisk->pvErrorUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
145 va_end(va);
146 return rc;
147}
148
149/**
150 * internal: add image structure to the end of images list.
151 */
152static void vdAddImageToList(PVBOXHDD pDisk, PVBOXHDDIMAGEDESC pImage)
153{
154 pImage->pPrev = NULL;
155 pImage->pNext = NULL;
156
157 if (pDisk->pBase)
158 {
159 Assert(pDisk->cImages > 0);
160 pImage->pPrev = pDisk->pLast;
161 pDisk->pLast->pNext = pImage;
162 pDisk->pLast = pImage;
163 }
164 else
165 {
166 Assert(pDisk->cImages == 0);
167 pDisk->pBase = pImage;
168 pDisk->pLast = pImage;
169 }
170
171 pDisk->cImages++;
172}
173
174/**
175 * internal: remove image structure from the images list.
176 */
177static void vdRemoveImageFromList(PVBOXHDD pDisk, PVBOXHDDIMAGEDESC pImage)
178{
179 Assert(pDisk->cImages > 0);
180
181 if (pImage->pPrev)
182 pImage->pPrev->pNext = pImage->pNext;
183 else
184 pDisk->pBase = pImage->pNext;
185
186 if (pImage->pNext)
187 pImage->pNext->pPrev = pImage->pPrev;
188 else
189 pDisk->pLast = pImage->pPrev;
190
191 pImage->pPrev = NULL;
192 pImage->pNext = NULL;
193
194 pDisk->cImages--;
195}
196
197/**
198 * internal: find image by index into the images list.
199 */
200static PVBOXHDDIMAGEDESC vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
201{
202 PVBOXHDDIMAGEDESC pImage = pDisk->pBase;
203 while (pImage && nImage)
204 {
205 pImage = pImage->pNext;
206 nImage--;
207 }
208 return pImage;
209}
210
211/**
212 * internal: read the specified amount of data in whatever blocks the backend
213 * will give us.
214 */
215static int vdReadHelper(PVBOXHDD pDisk, PVBOXHDDIMAGEDESC pImage, uint64_t uOffset, void *pvBuf, size_t cbRead)
216{
217 int rc;
218 size_t cbThisRead;
219
220 /* Loop until all read. */
221 do
222 {
223 /* Search for image with allocated block. Do not attempt to read more
224 * than the previous reads marked as valid. Otherwise this would return
225 * stale data when different block sizes are used for the images. */
226 cbThisRead = cbRead;
227 rc = VINF_VDI_BLOCK_FREE;
228 for (; pImage != NULL && rc == VINF_VDI_BLOCK_FREE; pImage = pImage->pPrev)
229 {
230 rc = pDisk->Backend->pfnRead(pImage->pvBackendData, uOffset, pvBuf, cbThisRead, &cbThisRead);
231 }
232
233 /* No image in the chain contains the data for the block. */
234 if (rc == VINF_VDI_BLOCK_FREE)
235 {
236 memset(pvBuf, '\0', cbThisRead);
237 rc = VINF_SUCCESS;
238 }
239
240 cbRead -= cbThisRead;
241 uOffset += cbThisRead;
242 pvBuf = (char *)pvBuf + cbThisRead;
243 } while (cbRead != 0 && VBOX_SUCCESS(rc));
244
245 return rc;
246}
247
248/**
249 * internal: mark the disk as not modified.
250 */
251static void vdResetModifiedFlag(PVBOXHDD pDisk)
252{
253 if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
254 {
255 /* generate new last-modified uuid */
256 if (!(pDisk->uModified | VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
257 {
258 RTUUID Uuid;
259
260 RTUuidCreate(&Uuid);
261 pDisk->Backend->pfnSetModificationUuid(pDisk->pLast->pvBackendData, &Uuid);
262 }
263
264 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
265 }
266}
267
268/**
269 * internal: mark the disk as modified.
270 */
271static void vdSetModifiedFlag(PVBOXHDD pDisk)
272{
273 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
274 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
275 {
276 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
277
278 /* First modify, so create a UUID and ensure it's written to disk. */
279 vdResetModifiedFlag(pDisk);
280
281 if (!(pDisk->uModified | VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
282 pDisk->Backend->pfnFlush(pDisk->pLast->pvBackendData);
283 }
284}
285
286/**
287 * Allocates and initializes an empty VBox HDD container.
288 * No image files are opened.
289 *
290 * @returns VBox status code.
291 * @param pszBackend Name of the image file backend to use.
292 * @param pfnError Callback for setting extended error information.
293 * @param pvErrorUser Opaque parameter for pfnError.
294 * @param ppDisk Where to store the reference to the VBox HDD container.
295 */
296VBOXDDU_DECL(int) VDCreate(const char *pszBackend, PFNVDERROR pfnError, void *pvErrorUser, PVBOXHDD *ppDisk)
297{
298 int rc = VINF_SUCCESS;
299 PVBOXHDDBACKEND pBackend = NULL;
300 PVBOXHDD pDisk = NULL;
301
302 /* Find backend. */
303 for (unsigned i = 0; aBackends[i].pszBackendName != NULL; i++)
304 {
305 if (!strcmp(pszBackend, aBackends[i].pszBackendName))
306 {
307 pBackend = aBackends[i].Backend;
308 break;
309 }
310 }
311 if (pBackend)
312 {
313 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
314 if (pDisk)
315 {
316 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
317 pDisk->cImages = 0;
318 pDisk->pBase = NULL;
319 pDisk->pLast = NULL;
320 pDisk->cbSize = 0;
321 pDisk->cCylinders = 0;
322 pDisk->cHeads = 0;
323 pDisk->cSectors = 0;
324 pDisk->pfnError = pfnError;
325 pDisk->pvErrorUser = pvErrorUser;
326 pDisk->Backend = pBackend;
327 *ppDisk = pDisk;
328 }
329 else
330 rc = VERR_NO_MEMORY;
331 }
332 else
333 rc = vdEarlyError(pfnError, pvErrorUser, VERR_INVALID_PARAMETER, RT_SRC_POS, "VD: unknown backend name '%s'", pszBackend);
334
335 LogFlow(("%s: returns %vrc (pDisk=%#p)\n", __FUNCTION__, rc, pDisk));
336 return rc;
337}
338
339/**
340 * Destroys the VBox HDD container.
341 * If container has opened image files they will be closed.
342 *
343 * @param pDisk Pointer to VBox HDD container.
344 */
345VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
346{
347 LogFlow(("%s: pDisk=%#p\n", pDisk));
348 /* sanity check */
349 Assert(pDisk);
350 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
351
352 if (pDisk)
353 {
354 VDCloseAll(pDisk);
355 RTMemFree(pDisk);
356 }
357}
358
359/**
360 * Opens an image file.
361 *
362 * The first opened image file in a HDD container must have a base image type,
363 * others (next opened images) must be a differencing or undo images.
364 * Linkage is checked for differencing image to be in consistence with the previously opened image.
365 * When another differencing image is opened and the last image was opened in read/write access
366 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
367 * other processes to use images in read-only mode too.
368 *
369 * Note that the image can be opened in read-only mode if a read/write open is not possible.
370 * Use VDIsReadOnly to check open mode.
371 *
372 * @returns VBox status code.
373 * @param pDisk Pointer to VBox HDD container.
374 * @param pszFilename Name of the image file to open.
375 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
376 */
377VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszFilename, unsigned uOpenFlags)
378{
379 int rc = VINF_SUCCESS;
380 LogFlow(("%s: pszFilename=\"%s\" uOpenFlags=%#x\n", __FUNCTION__, pszFilename, uOpenFlags));
381 /* sanity check */
382 Assert(pDisk);
383 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
384
385 /* Check arguments. */
386 if ( !pszFilename
387 || *pszFilename == '\0'
388 || (uOpenFlags & ~VD_OPEN_FLAGS_MASK))
389 {
390 AssertMsgFailed(("Invalid arguments: pszFilename=%#p uOpenFlags=%#x\n", pszFilename, uOpenFlags));
391 return VERR_INVALID_PARAMETER;
392 }
393
394 /* Set up image descriptor. */
395 PVBOXHDDIMAGEDESC pImage = (PVBOXHDDIMAGEDESC)RTMemAllocZ(sizeof(VBOXHDDIMAGEDESC));
396 if (!pImage)
397 return VERR_NO_MEMORY;
398 pImage->pszFilename = RTStrDup(pszFilename);
399 if (!pImage->pszFilename)
400 rc = VERR_NO_MEMORY;
401
402 if (VBOX_SUCCESS(rc))
403 rc = pDisk->Backend->pfnOpen(pImage->pszFilename, uOpenFlags, pDisk->pfnError, pDisk->pvErrorUser, &pImage->pvBackendData);
404 /* If the open in read-write mode failed, retry in read-only mode. */
405 if (VBOX_FAILURE(rc))
406 {
407 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
408 && (rc == VERR_ACCESS_DENIED
409 || rc == VERR_PERMISSION_DENIED
410 || rc == VERR_WRITE_PROTECT
411 || rc == VERR_SHARING_VIOLATION
412 || rc == VERR_FILE_LOCK_FAILED))
413 rc = pDisk->Backend->pfnOpen(pImage->pszFilename, uOpenFlags | VD_OPEN_FLAGS_READONLY, pDisk->pfnError, pDisk->pvErrorUser, &pImage->pvBackendData);
414 if (VBOX_FAILURE(rc))
415 rc = vdError(pDisk, rc, RT_SRC_POS, N_("VD: error opening image file '%s'"), pszFilename);
416 }
417
418 if (VBOX_SUCCESS(rc))
419 {
420 VDIMAGETYPE enmImageType;
421 rc = pDisk->Backend->pfnGetImageType(pImage->pvBackendData, &enmImageType);
422 /* Check image type. As the image itself has no idea whether it's a
423 * base image or not, this info is derived here. Image 0 can be fixed
424 * or normal, all others must be normal images. */
425 if ( VBOX_SUCCESS(rc)
426 && pDisk->cImages != 0
427 && enmImageType != VD_IMAGE_TYPE_NORMAL)
428 rc = VERR_VDI_INVALID_TYPE;
429
430 if (VBOX_SUCCESS(rc))
431 {
432 uint64_t cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
433 if (pDisk->cImages == 0)
434 {
435 /* Cache disk information. */
436 pDisk->cbSize = cbSize;
437
438 /* Cache CHS geometry. */
439 int rc2 = pDisk->Backend->pfnGetGeometry(pImage->pvBackendData,
440 &pDisk->cCylinders,
441 &pDisk->cHeads,
442 &pDisk->cSectors);
443 if (VBOX_FAILURE(rc2))
444 {
445 pDisk->cCylinders = 0;
446 pDisk->cHeads = 0;
447 pDisk->cSectors = 0;
448 }
449 else
450 {
451 /* Make sure the CHS geometry is properly clipped. */
452 pDisk->cCylinders = RT_MIN(pDisk->cCylinders, 16383);
453 pDisk->cHeads = RT_MIN(pDisk->cHeads, 255);
454 pDisk->cSectors = RT_MIN(pDisk->cSectors, 255);
455 }
456
457 /* Cache translation mode. */
458 rc2 = pDisk->Backend->pfnGetTranslation(pImage->pvBackendData,
459 &pDisk->enmTranslation);
460 if (VBOX_FAILURE(rc2))
461 pDisk->enmTranslation = (PDMBIOSTRANSLATION)0;
462 }
463 else
464 {
465 /* Check image size/block size for consistency. */
466 if (cbSize != pDisk->cbSize)
467 rc = VERR_VDI_INVALID_TYPE;
468 }
469 }
470
471 if (VBOX_SUCCESS(rc) && pDisk->cImages != 0)
472 {
473 /* Switch previous image to read-only mode. */
474 unsigned uOpenFlagsPrevImg;
475 uOpenFlagsPrevImg = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
476 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
477 {
478 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
479 rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
480 }
481 }
482
483 if (VBOX_SUCCESS(rc))
484 {
485 /* Image successfully opened, make it the last image. */
486 vdAddImageToList(pDisk, pImage);
487 }
488
489 if (VBOX_FAILURE(rc))
490 {
491 /* Error detected, but image opened. Close image. */
492 int rc2;
493 rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData);
494 AssertRC(rc2);
495 pImage->pvBackendData = NULL;
496 }
497 }
498
499 if (VBOX_FAILURE(rc))
500 {
501 if (pImage)
502 {
503 if (pImage->pszFilename)
504 RTStrFree(pImage->pszFilename);
505 RTMemFree(pImage);
506 }
507 }
508
509 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
510 return rc;
511}
512
513/**
514 * Creates and opens a new base image file.
515 *
516 * @returns VBox status code.
517 * @param pDisk Pointer to VBox HDD container.
518 * @param pszFilename Name of the image file to create.
519 * @param enmType Image type, only base image types are acceptable.
520 * @param cbSize Image size in bytes.
521 * @param uImageFlags Flags specifying special image features.
522 * @param pszComment Pointer to image comment. NULL is ok.
523 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
524 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
525 * @param pvUser User argument for the progress callback.
526 */
527VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszFilename,
528 VDIMAGETYPE enmType, uint64_t cbSize,
529 unsigned uImageFlags, const char *pszComment,
530 unsigned uOpenFlags,
531 PFNVMPROGRESS pfnProgress, void *pvUser)
532{
533 return VERR_NOT_IMPLEMENTED;
534}
535
536/**
537 * Creates and opens a new differencing image file in HDD container.
538 * See comments for VDOpen function about differencing images.
539 *
540 * @returns VBox status code.
541 * @param pDisk Pointer to VBox HDD container.
542 * @param pszFilename Name of the differencing image file to create.
543 * @param uImageFlags Flags specifying special image features.
544 * @param pszComment Pointer to image comment. NULL is ok.
545 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
546 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
547 * @param pvUser User argument for the progress callback.
548 */
549VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszFilename,
550 unsigned uImageFlags, const char *pszComment,
551 unsigned uOpenFlags,
552 PFNVMPROGRESS pfnProgress, void *pvUser)
553{
554 return VERR_NOT_IMPLEMENTED;
555}
556
557/**
558 * Merges two images having a parent/child relationship (both directions).
559 * As a side effect the source image is deleted from both the disk and
560 * the images in the VBox HDD container.
561 *
562 * @returns VBox status code.
563 * @param pDisk Pointer to VBox HDD container.
564 * @param nImageFrom Name of the image file to merge from.
565 * @param nImageTo Name of the image file to merge to.
566 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
567 * @param pvUser User argument for the progress callback.
568 */
569VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom, unsigned nImageTo,
570 PFNVMPROGRESS pfnProgress, void *pvUser)
571{
572 return VERR_NOT_IMPLEMENTED;
573}
574
575/**
576 * Copies an image from one VBox HDD container to another.
577 * The copy is opened in the target VBox HDD container.
578 * It is possible to convert between different image formats, because the
579 * backend for the destination VBox HDD container may be different from the
580 * source container.
581 * If both the source and destination reference the same VBox HDD container,
582 * then the image is moved (by copying/deleting) to the new location.
583 * The source container is unchanged if the move operation fails, otherwise
584 * the image at the new location is opened in the same way as the old one was.
585 *
586 * @returns VBox status code.
587 * @param pDiskFrom Pointer to source VBox HDD container.
588 * @param nImage Image number, counts from 0. 0 is always base image of container.
589 * @param pDiskTo Pointer to destination VBox HDD container.
590 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
591 * @param pvUser User argument for the progress callback.
592 */
593VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
594 PFNVMPROGRESS pfnProgress, void *pvUser)
595{
596 return VERR_NOT_IMPLEMENTED;
597}
598
599/**
600 * Compacts a growing image file by removing zeroed data blocks.
601 * Optionally defragments data in the image so that ascending sector numbers
602 * are stored in ascending location in the image file.
603 *
604 * @todo maybe include this function in VDCopy.
605 *
606 * @returns VBox status code.
607 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
608 * @param pDisk Pointer to VBox HDD container.
609 * @param nImage Image number, counts from 0. 0 is always base image of container.
610 * @param fDefragment If true, reorder file data so that sectors are stored in ascending order.
611 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
612 * @param pvUser User argument for the progress callback.
613 */
614VBOXDDU_DECL(int) VDCompact(PVBOXHDD pDisk, unsigned nImage,
615 bool fDefragment,
616 PFNVMPROGRESS pfnProgress, void *pvUser)
617{
618 return VERR_NOT_IMPLEMENTED;
619}
620
621/**
622 * Resizes an image. Allows setting the disk size to both larger and smaller
623 * values than the current disk size.
624 *
625 * @returns VBox status code.
626 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
627 * @param pDisk Pointer to VBox HDD container.
628 * @param nImage Image number, counts from 0. 0 is always base image of container.
629 * @param cbSize New image size in bytes.
630 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
631 * @param pvUser User argument for the progress callback.
632 */
633VBOXDDU_DECL(int) VDResize(PVBOXHDD pDisk, unsigned nImage, uint64_t cbSize,
634 PFNVMPROGRESS pfnProgress, void *pvUser)
635{
636 return VERR_NOT_IMPLEMENTED;
637}
638
639/**
640 * Closes the last opened image file in the HDD container. Leaves all changes inside it.
641 * If previous image file was opened in read-only mode (that is normal) and closing image
642 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
643 * will be reopened in read/write mode.
644 *
645 * @param pDisk Pointer to VBox HDD container.
646 * @param fDelete If true, delete the image from the host disk.
647 */
648VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
649{
650 return VERR_NOT_IMPLEMENTED;
651}
652
653/**
654 * Closes all opened image files in HDD container.
655 *
656 * @param pDisk Pointer to VDI HDD container.
657 */
658VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
659{
660 LogFlow(("%s:\n", __FUNCTION__));
661 /* sanity check */
662 Assert(pDisk);
663 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
664
665 PVBOXHDDIMAGEDESC pImage = pDisk->pLast;
666 int rc = VINF_SUCCESS;
667 while (pImage)
668 {
669 PVBOXHDDIMAGEDESC pPrev = pImage->pPrev;
670 /* Remove image from list of opened images. */
671 vdRemoveImageFromList(pDisk, pImage);
672 /* Close image. */
673 int rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData);
674 if (VBOX_FAILURE(rc2) && VBOX_SUCCESS(rc))
675 rc = rc2;
676 /* Free remaining resources related to the image. */
677 RTStrFree(pImage->pszFilename);
678 RTMemFree(pImage);
679 pImage = pPrev;
680 }
681 Assert(pDisk->pLast == NULL);
682
683 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
684 return rc;
685}
686
687/**
688 * Read data from virtual HDD.
689 *
690 * @returns VBox status code.
691 * @param pDisk Pointer to VBox HDD container.
692 * @param uOffset Offset of first reading byte from start of disk.
693 * @param pvBuf Pointer to buffer for reading data.
694 * @param cbRead Number of bytes to read.
695 */
696VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf, size_t cbRead)
697{
698 /* sanity check */
699 Assert(pDisk);
700 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
701
702 int rc = VINF_SUCCESS;
703 PVBOXHDDIMAGEDESC pImage = pDisk->pLast;
704 if (RT_UNLIKELY(!pImage))
705 {
706 Assert(pImage);
707 rc = VERR_VDI_NOT_OPENED;
708 goto out;
709 }
710
711 /* Check params. */
712 if (uOffset + cbRead > pDisk->cbSize || cbRead == 0)
713 {
714 AssertMsgFailed(("uOffset=%llu cbRead=%u\n", uOffset, cbRead));
715 return VERR_INVALID_PARAMETER;
716 }
717
718 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead);
719out:
720 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
721 return rc;
722}
723
724/**
725 * Write data to virtual HDD.
726 *
727 * @returns VBox status code.
728 * @param pDisk Pointer to VBox HDD container.
729 * @param uOffset Offset of first reading byte from start of disk.
730 * @param pvBuf Pointer to buffer for writing data.
731 * @param cbWrite Number of bytes to write.
732 */
733VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf, size_t cbWrite)
734{
735 /* sanity check */
736 Assert(pDisk);
737 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
738
739 int rc = VINF_SUCCESS;
740 size_t cbThisWrite;
741 size_t cbPreRead, cbPostRead;
742 PVBOXHDDIMAGEDESC pImage = pDisk->pLast;
743 if (RT_UNLIKELY(!pImage))
744 {
745 Assert(pImage);
746 rc = VERR_VDI_NOT_OPENED;
747 goto out;
748 }
749
750 /* Check params. */
751 if (uOffset + cbWrite > pDisk->cbSize || cbWrite == 0)
752 {
753 AssertMsgFailed(("uOffset=%llu cbWrite=%u\n", uOffset, cbWrite));
754 rc = VERR_INVALID_PARAMETER;
755 goto out;
756 }
757
758 vdSetModifiedFlag(pDisk);
759
760 /* Loop until all written. */
761 do
762 {
763 /* Try to write the possibly partial block to the last opened image.
764 * This works when the block is already allocated in this image or
765 * if it is a full-block write, which automatically allocates a new
766 * block if needed. */
767 cbThisWrite = cbWrite;
768 rc = pDisk->Backend->pfnWrite(pImage->pvBackendData, uOffset, pvBuf, cbThisWrite, &cbThisWrite, &cbPreRead, &cbPostRead);
769 if (rc == VINF_VDI_BLOCK_FREE)
770 {
771 /* Partial block write to an unallocated block. Read the rest of
772 * this block and then do a full-block (allocating) write. */
773 void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
774 if (!pvBuf)
775 {
776 Assert(!pvBuf);
777 rc = VERR_NO_MEMORY;
778 break;
779 }
780
781 /* Read the data that goes before the write to fill the block. */
782 if (cbPreRead)
783 {
784 rc = vdReadHelper(pDisk, pImage->pPrev, uOffset - cbPreRead,
785 pvTmp, cbPreRead);
786 if (VBOX_FAILURE(rc))
787 break;
788 }
789
790 /* Copy the data to the right place in the buffer. */
791 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
792
793 /* Read the data that goes after the write to fill the block. */
794 if (cbPostRead)
795 {
796 /* If we have data to be written, use that instead of reading
797 * data from the image. */
798 size_t cbWriteCopy;
799 if (cbWrite > cbThisWrite)
800 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
801 else
802 cbWriteCopy = 0;
803 /* Figure out how much we cannnot read from the image, because
804 * the last block to write might exceed the nominal size of the
805 * image for technical reasons. */
806 size_t cbFill;
807 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
808 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
809 else
810 cbFill = 0;
811 /* The rest must be read from the image. */
812 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
813
814 /* Now assemble the remaining data. */
815 if (cbWriteCopy)
816 memcpy((char *)pvTmp + cbPreRead + cbThisWrite, (char *)pvBuf + cbThisWrite, cbWriteCopy);
817 if (cbReadImage)
818 rc = vdReadHelper(pDisk, pImage->pPrev, uOffset + cbThisWrite + cbWriteCopy,
819 (void *)((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy),
820 cbReadImage);
821 if (VBOX_FAILURE(rc))
822 break;
823 if (cbFill)
824 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage, '\0', cbFill);
825 }
826
827 /* Write the full block to the virtual disk. */
828 rc = pDisk->Backend->pfnWrite(pImage->pvBackendData,
829 uOffset - cbPreRead, pvTmp,
830 cbPreRead + cbThisWrite + cbPostRead,
831 NULL,
832 &cbPreRead, &cbPostRead);
833 Assert(rc != VINF_VDI_BLOCK_FREE);
834 }
835
836 cbWrite -= cbThisWrite;
837 uOffset += cbThisWrite;
838 pvBuf = (char *)pvBuf + cbThisWrite;
839 } while (cbWrite != 0 && VBOX_SUCCESS(rc));
840
841out:
842 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
843 return rc;
844}
845
846/**
847 * Make sure the on disk representation of a virtual HDD is up to date.
848 *
849 * @returns VBox status code.
850 * @param pDisk Pointer to VBox HDD container.
851 */
852VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
853{
854 /* sanity check */
855 Assert(pDisk);
856 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
857
858 int rc = VINF_SUCCESS;
859 PVBOXHDDIMAGEDESC pImage = pDisk->pLast;
860 if (RT_UNLIKELY(!pImage))
861 {
862 Assert(pImage);
863 rc = VERR_VDI_NOT_OPENED;
864 }
865 else
866 {
867 vdResetModifiedFlag(pDisk);
868 rc = pDisk->Backend->pfnFlush(pImage->pvBackendData);
869 }
870
871 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
872 return rc;
873}
874
875/**
876 * Get number of opened images in HDD container.
877 *
878 * @returns Number of opened images for HDD container. 0 if no images have been opened.
879 * @param pDisk Pointer to VBox HDD container.
880 */
881VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
882{
883 /* sanity check */
884 Assert(pDisk);
885 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
886
887 unsigned c = pDisk->cImages;
888 LogFlow(("%s: returns %d\n", __FUNCTION__, c));
889 return c;
890}
891
892/**
893 * Get read/write mode of the VBox HDD container.
894 *
895 * @returns Virtual disk ReadOnly status.
896 * @returns true if no image is opened in HDD container.
897 * @param pDisk Pointer to VBox HDD container.
898 */
899VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
900{
901 /* sanity check */
902 Assert(pDisk);
903 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
904
905 bool f;
906 if (pDisk->pLast)
907 {
908 unsigned uOpenFlags;
909 uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
910 f = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
911 }
912 else
913 {
914 AssertMsgFailed(("No disk image is opened!\n"));
915 f = true;
916 }
917
918 LogFlow(("%s: returns %d\n", __FUNCTION__, f));
919 return f;
920}
921
922/**
923 * Get total disk size of the VBox HDD container.
924 *
925 * @returns Virtual disk size in bytes.
926 * @returns 0 if no image is opened in HDD container.
927 * @param pDisk Pointer to VBox HDD container.
928 */
929VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk)
930{
931 /* sanity check */
932 Assert(pDisk);
933 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
934
935 uint64_t cb = pDisk->cbSize;
936 LogFlow(("%s: returns %lld\n", __FUNCTION__, cb));
937 return cb;
938}
939
940/**
941 * Get virtual disk geometry stored in HDD container.
942 *
943 * @returns VBox status code.
944 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
945 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
946 * @param pDisk Pointer to VBox HDD container.
947 * @param pcCylinders Where to store the number of cylinders. NULL is ok.
948 * @param pcHeads Where to store the number of heads. NULL is ok.
949 * @param pcSectors Where to store the number of sectors. NULL is ok.
950 */
951VBOXDDU_DECL(int) VDGetGeometry(PVBOXHDD pDisk,
952 unsigned *pcCylinders, unsigned *pcHeads, unsigned *pcSectors)
953{
954 /* sanity check */
955 Assert(pDisk);
956 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
957
958 int rc = VINF_SUCCESS;
959 PVBOXHDDIMAGEDESC pImage = pDisk->pBase;
960 if (RT_UNLIKELY(!pImage))
961 {
962 Assert(pImage);
963 rc = VERR_VDI_NOT_OPENED;
964 }
965 else
966 {
967 if (pDisk->cCylinders != 0)
968 {
969 if (pcCylinders)
970 *pcCylinders = pDisk->cCylinders;
971 if (pcHeads)
972 *pcHeads = pDisk->cHeads;
973 if (pcSectors)
974 *pcSectors = pDisk->cSectors;
975 }
976 else
977 rc = VERR_VDI_GEOMETRY_NOT_SET;
978 }
979 LogFlow(("%s: %Vrc (CHS=%u/%u/%u)\n", __FUNCTION__, rc,
980 pDisk->cCylinders, pDisk->cHeads, pDisk->cSectors));
981 return rc;
982}
983
984/**
985 * Store virtual disk geometry in HDD container.
986 *
987 * Note that in case of unrecoverable error all images in HDD container will be closed.
988 *
989 * @returns VBox status code.
990 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
991 * @param pDisk Pointer to VBox HDD container.
992 * @param cCylinders Number of cylinders.
993 * @param cHeads Number of heads.
994 * @param cSectors Number of sectors.
995 */
996VBOXDDU_DECL(int) VDSetGeometry(PVBOXHDD pDisk,
997 unsigned cCylinders, unsigned cHeads, unsigned cSectors)
998{
999 /* sanity check */
1000 Assert(pDisk);
1001 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1002
1003 int rc = VINF_SUCCESS;
1004 PVBOXHDDIMAGEDESC pImage = pDisk->pBase;
1005 if (RT_UNLIKELY(!pImage))
1006 {
1007 Assert(pImage);
1008 rc = VERR_VDI_NOT_OPENED;
1009 }
1010 else
1011 {
1012 if ( cCylinders != pDisk->cCylinders
1013 || cHeads != pDisk->cHeads
1014 || cSectors != pDisk->cSectors)
1015 {
1016 /* Only update geometry if it is changed. Avoids similar checks
1017 * in every backend. Most of the time the new geometry is set to
1018 * the previous values, so no need to go through the hassle of
1019 * updating an image which could be opened in read-only mode right
1020 * now. */
1021 rc = pDisk->Backend->pfnSetGeometry(pImage->pvBackendData,
1022 cCylinders, cHeads, cSectors);
1023
1024 /* Cache new geometry values in any case, whether successful or not. */
1025 int rc2 = pDisk->Backend->pfnGetGeometry(pImage->pvBackendData,
1026 &pDisk->cCylinders,
1027 &pDisk->cHeads,
1028 &pDisk->cSectors);
1029 if (VBOX_FAILURE(rc2))
1030 {
1031 pDisk->cCylinders = 0;
1032 pDisk->cHeads = 0;
1033 pDisk->cSectors = 0;
1034 }
1035 else
1036 {
1037 /* Make sure the CHS geometry is properly clipped. */
1038 pDisk->cCylinders = RT_MIN(pDisk->cCylinders, 16383);
1039 pDisk->cHeads = RT_MIN(pDisk->cHeads, 255);
1040 pDisk->cSectors = RT_MIN(pDisk->cSectors, 255);
1041 }
1042 }
1043 }
1044
1045 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1046 return rc;
1047}
1048
1049/**
1050 * Get virtual disk translation mode stored in HDD container.
1051 *
1052 * @returns VBox status code.
1053 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1054 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
1055 * @param pDisk Pointer to VBox HDD container.
1056 * @param penmTranslation Where to store the translation mode (see pdm.h).
1057 */
1058VBOXDDU_DECL(int) VDGetTranslation(PVBOXHDD pDisk, PPDMBIOSTRANSLATION penmTranslation)
1059{
1060 /* sanity check */
1061 Assert(pDisk);
1062 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1063
1064 int rc = VINF_SUCCESS;
1065 PVBOXHDDIMAGEDESC pImage = pDisk->pBase;
1066 if (RT_UNLIKELY(!pImage))
1067 {
1068 Assert(pImage);
1069 rc = VERR_VDI_NOT_OPENED;
1070 }
1071 else
1072 {
1073 if (pDisk->enmTranslation != 0)
1074 *penmTranslation = pDisk->enmTranslation;
1075 else
1076 rc = VERR_VDI_GEOMETRY_NOT_SET;
1077 }
1078 LogFlow(("%s: %Vrc (translation=%u)\n", __FUNCTION__, rc,
1079 pDisk->enmTranslation));
1080 return rc;
1081}
1082
1083/**
1084 * Store virtual disk translation mode in HDD container.
1085 *
1086 * Note that in case of unrecoverable error all images in HDD container will be closed.
1087 *
1088 * @returns VBox status code.
1089 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1090 * @param pDisk Pointer to VBox HDD container.
1091 * @param enmTranslation Translation mode (see pdm.h).
1092 */
1093VBOXDDU_DECL(int) VDSetTranslation(PVBOXHDD pDisk, PDMBIOSTRANSLATION enmTranslation)
1094{
1095 /* sanity check */
1096 Assert(pDisk);
1097 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1098
1099 int rc = VINF_SUCCESS;
1100 PVBOXHDDIMAGEDESC pImage = pDisk->pBase;
1101 if (RT_UNLIKELY(!pImage))
1102 {
1103 Assert(pImage);
1104 rc = VERR_VDI_NOT_OPENED;
1105 }
1106 else
1107 {
1108 if (enmTranslation == 0)
1109 rc = VERR_INVALID_PARAMETER;
1110 else if (enmTranslation != pDisk->enmTranslation)
1111 {
1112 /* Only update translation mode if it is changed. Avoids similar
1113 * checks in every backend. Most of the time the new translation
1114 * mode is set to the previous value, so no need to go through the
1115 * hassle of updating an image which could be opened in read-only
1116 * mode right now. */
1117 rc = pDisk->Backend->pfnSetTranslation(pImage->pvBackendData,
1118 enmTranslation);
1119
1120 /* Cache new translation mode in any case, whether successful or not. */
1121 int rc2 = pDisk->Backend->pfnGetTranslation(pImage->pvBackendData,
1122 &pDisk->enmTranslation);
1123 if (VBOX_FAILURE(rc2))
1124 pDisk->enmTranslation = (PDMBIOSTRANSLATION)0;
1125 }
1126 }
1127
1128 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1129 return rc;
1130}
1131
1132/**
1133 * Get version of image in HDD container.
1134 *
1135 * @returns VBox status code.
1136 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1137 * @param pDisk Pointer to VBox HDD container.
1138 * @param nImage Image number, counts from 0. 0 is always base image of container.
1139 * @param puVersion Where to store the image version.
1140 */
1141VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage, unsigned *puVersion)
1142{
1143 return VERR_NOT_IMPLEMENTED;
1144}
1145
1146/**
1147 * Get type of image in HDD container.
1148 *
1149 * @returns VBox status code.
1150 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1151 * @param pDisk Pointer to VBox HDD container.
1152 * @param nImage Image number, counts from 0. 0 is always base image of container.
1153 * @param penmType Where to store the image type.
1154 */
1155VBOXDDU_DECL(int) VDGetImageType(PVBOXHDD pDisk, unsigned nImage, PVDIMAGETYPE penmType)
1156{
1157 return VERR_NOT_IMPLEMENTED;
1158}
1159
1160/**
1161 * Get flags of image in HDD container.
1162 *
1163 * @returns VBox status code.
1164 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1165 * @param pDisk Pointer to VBox HDD container.
1166 * @param nImage Image number, counts from 0. 0 is always base image of container.
1167 * @param puImageFlags Where to store the image flags.
1168 */
1169VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage, unsigned *puImageFlags)
1170{
1171 return VERR_NOT_IMPLEMENTED;
1172}
1173
1174/**
1175 * Get open flags of last opened image in HDD container.
1176 *
1177 * @returns VBox status code.
1178 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1179 * @param pDisk Pointer to VBox HDD container.
1180 * @param puOpenFlags Where to store the image open flags.
1181 */
1182VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned *puOpenFlags)
1183{
1184 /* sanity check */
1185 Assert(pDisk);
1186 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1187
1188 unsigned uOpenFlags = 0;
1189 int rc = VINF_SUCCESS;
1190 PVBOXHDDIMAGEDESC pImage = pDisk->pLast;
1191 if (RT_UNLIKELY(!pImage))
1192 {
1193 Assert(pImage);
1194 rc = VERR_VDI_NOT_OPENED;
1195 }
1196 else
1197 {
1198 uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1199 *puOpenFlags = uOpenFlags;
1200 }
1201 LogFlow(("%s: returns %Vrc uOpenFlags=%#x\n", __FUNCTION__, rc, uOpenFlags));
1202 return uOpenFlags;
1203}
1204
1205/**
1206 * Set open flags of last opened image in HDD container.
1207 * This operation may cause file locking changes and/or files being reopened.
1208 * Note that in case of unrecoverable error all images in HDD container will be closed.
1209 *
1210 * @returns Virtual disk block size in bytes.
1211 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1212 * @returns VBox status code.
1213 * @param pDisk Pointer to VBox HDD container.
1214 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1215 */
1216VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned uOpenFlags)
1217{
1218 /* sanity check */
1219 Assert(pDisk);
1220 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1221
1222 int rc = VINF_SUCCESS;
1223 PVBOXHDDIMAGEDESC pImage = pDisk->pLast;
1224 if (RT_UNLIKELY(!pImage))
1225 {
1226 Assert(pImage);
1227 rc = VERR_VDI_NOT_OPENED;
1228 }
1229 else
1230 rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlags);
1231 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1232 return rc;
1233}
1234
1235/**
1236 * Get base filename of image in HDD container. Some image formats use
1237 * other filenames as well, so don't use this for anything but for informational
1238 * purposes.
1239 *
1240 * @returns VBox status code.
1241 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1242 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
1243 * @param pDisk Pointer to VBox HDD container.
1244 * @param nImage Image number, counts from 0. 0 is always base image of container.
1245 * @param pszFilename Where to store the image file name.
1246 * @param cbFilename Size of buffer pszFilename points to.
1247 */
1248VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage, char *pszFilename, unsigned cbFilename)
1249{
1250 return VERR_NOT_IMPLEMENTED;
1251}
1252
1253/**
1254 * Get the comment line of image in HDD container.
1255 *
1256 * @returns VBox status code.
1257 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1258 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
1259 * @param pDisk Pointer to VBox HDD container.
1260 * @param nImage Image number, counts from 0. 0 is always base image of container.
1261 * @param pszComment Where to store the comment string of image. NULL is ok.
1262 * @param cbComment The size of pszComment buffer. 0 is ok.
1263 */
1264VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage, char *pszComment, unsigned cbComment)
1265{
1266 return VERR_NOT_IMPLEMENTED;
1267}
1268
1269/**
1270 * * Changes the comment line of image in HDD container.
1271 * *
1272 * * @returns VBox status code.
1273 * * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1274 * * @param pDisk Pointer to VBox HDD container.
1275 * * @param nImage Image number, counts from 0. 0 is always base image of container.
1276 * * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
1277 * */
1278VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage, const char *pszComment)
1279{
1280 return VERR_NOT_IMPLEMENTED;
1281}
1282
1283
1284/**
1285 * Get UUID of image in HDD container.
1286 *
1287 * @returns VBox status code.
1288 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1289 * @param pDisk Pointer to VBox HDD container.
1290 * @param nImage Image number, counts from 0. 0 is always base image of container.
1291 * @param pUuid Where to store the image creation UUID.
1292 */
1293VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
1294{
1295 /* sanity check */
1296 Assert(pDisk);
1297 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1298 Assert(pUuid);
1299
1300 PVBOXHDDIMAGEDESC pImage = vdGetImageByNumber(pDisk, nImage);
1301 int rc;
1302 if (pImage)
1303 rc = pDisk->Backend->pfnGetUuid(pImage, pUuid);
1304 else
1305 rc = VERR_VDI_IMAGE_NOT_FOUND;
1306
1307 LogFlow(("%s: returns %Vrc, uuid={%Vuuid} nImage=%u\n", __FUNCTION__,
1308 rc, pUuid, nImage));
1309 return rc;
1310}
1311
1312/**
1313 * Set the image's UUID. Should not be used by normal applications.
1314 *
1315 * @returns VBox status code.
1316 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1317 * @param pDisk Pointer to VBox HDD container.
1318 * @param nImage Image number, counts from 0. 0 is always base image of container.
1319 * @param pUuid Optional parameter, new UUID of the image.
1320 */
1321VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
1322{
1323 LogFlow(("%s: uuid={%Vuuid} nImage=%u\n", __FUNCTION__, pUuid, nImage));
1324 /* sanity check */
1325 Assert(pDisk);
1326 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1327 Assert(pUuid);
1328
1329 PVBOXHDDIMAGEDESC pImage = vdGetImageByNumber(pDisk, nImage);
1330 int rc;
1331 if (pImage)
1332 rc = pDisk->Backend->pfnSetUuid(pImage, pUuid);
1333 else
1334 rc = VERR_VDI_IMAGE_NOT_FOUND;
1335
1336 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1337 return rc;
1338}
1339
1340/**
1341 * Get last modification UUID of image in HDD container.
1342 *
1343 * @returns VBox status code.
1344 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1345 * @param pDisk Pointer to VBox HDD container.
1346 * @param nImage Image number, counts from 0. 0 is always base image of container.
1347 * @param pUuid Where to store the image modification UUID.
1348 */
1349VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
1350{
1351 /* sanity check */
1352 Assert(pDisk);
1353 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1354 Assert(pUuid);
1355
1356 PVBOXHDDIMAGEDESC pImage = vdGetImageByNumber(pDisk, nImage);
1357 int rc;
1358 if (pImage)
1359 rc = pDisk->Backend->pfnGetModificationUuid(pImage, pUuid);
1360 else
1361 rc = VERR_VDI_IMAGE_NOT_FOUND;
1362
1363 LogFlow(("%s: returns %Vrc, uuid={%Vuuid} nImage=%u\n", __FUNCTION__,
1364 rc, pUuid, nImage));
1365 return rc;
1366}
1367
1368/**
1369 * Set the image's last modification UUID. Should not be used by normal applications.
1370 *
1371 * @returns VBox status code.
1372 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1373 * @param pDisk Pointer to VBox HDD container.
1374 * @param nImage Image number, counts from 0. 0 is always base image of container.
1375 * @param pUuid Optional parameter, new last modification UUID of the image.
1376 */
1377VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
1378{
1379 LogFlow(("%s: uuid={%Vuuid} nImage=%u\n", __FUNCTION__, pUuid, nImage));
1380 /* sanity check */
1381 Assert(pDisk);
1382 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1383 Assert(pUuid);
1384
1385 PVBOXHDDIMAGEDESC pImage = vdGetImageByNumber(pDisk, nImage);
1386 int rc;
1387 if (pImage)
1388 rc = pDisk->Backend->pfnSetModificationUuid(pImage, pUuid);
1389 else
1390 rc = VERR_VDI_IMAGE_NOT_FOUND;
1391
1392 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1393 return rc;
1394}
1395
1396/**
1397 * Get parent UUID of image in HDD container.
1398 *
1399 * @returns VBox status code.
1400 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1401 * @param pDisk Pointer to VBox HDD container.
1402 * @param nImage Image number, counts from 0. 0 is always base image of container.
1403 * @param pUuid Where to store the parent image UUID.
1404 */
1405VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
1406{
1407 /* sanity check */
1408 Assert(pDisk);
1409 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1410 Assert(pUuid);
1411
1412 PVBOXHDDIMAGEDESC pImage = vdGetImageByNumber(pDisk, nImage);
1413 int rc;
1414 if (pImage)
1415 rc = pDisk->Backend->pfnGetParentUuid(pImage, pUuid);
1416 else
1417 rc = VERR_VDI_IMAGE_NOT_FOUND;
1418
1419 LogFlow(("%s: returns %Vrc, uuid={%Vuuid} nImage=%u\n", __FUNCTION__,
1420 rc, pUuid, nImage));
1421 return rc;
1422}
1423
1424/**
1425 * Set the image's parent UUID. Should not be used by normal applications.
1426 *
1427 * @returns VBox status code.
1428 * @param pDisk Pointer to VBox HDD container.
1429 * @param nImage Image number, counts from 0. 0 is always base image of container.
1430 * @param pUuid Optional parameter, new parent UUID of the image.
1431 */
1432VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
1433{
1434 LogFlow(("%s: uuid={%Vuuid} nImage=%u\n", __FUNCTION__, pUuid, nImage));
1435 /* sanity check */
1436 Assert(pDisk);
1437 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1438 Assert(pUuid);
1439
1440 PVBOXHDDIMAGEDESC pImage = vdGetImageByNumber(pDisk, nImage);
1441 int rc;
1442 if (pImage)
1443 rc = pDisk->Backend->pfnSetParentUuid(pImage, pUuid);
1444 else
1445 rc = VERR_VDI_IMAGE_NOT_FOUND;
1446
1447 LogFlow(("%s: returns %Vrc\n", __FUNCTION__, rc));
1448 return rc;
1449}
1450
1451
1452/**
1453 * Debug helper - dumps all opened images in HDD container into the log file.
1454 *
1455 * @param pDisk Pointer to VDI HDD container.
1456 */
1457VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
1458{
1459 /* sanity check */
1460 Assert(pDisk);
1461 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1462
1463 RTLogPrintf("--- Dumping VDI Disk, Images=%u\n", pDisk->cImages);
1464 for (PVBOXHDDIMAGEDESC pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
1465 {
1466 RTLogPrintf("Dumping VDI image \"%s\" file=%#p\n", pImage->pszFilename);
1467 /** @todo call backend to print its part. */
1468 }
1469}
1470
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