VirtualBox

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

Last change on this file since 2583 was 2564, checked in by vboxsync, 18 years ago

Many VMDK block splitup fixes and the start of the create image
function.

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