VirtualBox

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

Last change on this file since 6291 was 6291, checked in by vboxsync, 17 years ago

Big virtual disk changeset containing several modifications

  • remove the always buggy translation setting and replace it with two sets of geometries, physical and logical
  • complete vmdk creation (fixed/dynamic variants, both split in 2G chunks and single file)
  • implemented VBoxHDD-new generic snapshot support, i.e. diff image creation and image merging (completely untested, I'm pretty sure there are bugs)
  • assorted changes which generalize the VBoxHDD-new interfaces (both externally and internally)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 101.6 KB
Line 
1/** $Id: VBoxHDD-new.cpp 6291 2008-01-09 10:57:05Z vboxsync $ */
2/** @file
3 * VBox HDD Container implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2008 innotek 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 (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_VD
22#include <VBox/VBoxHDD-new.h>
23#include <VBox/err.h>
24
25#include <VBox/log.h>
26#include <iprt/alloc.h>
27#include <iprt/assert.h>
28#include <iprt/uuid.h>
29#include <iprt/file.h>
30#include <iprt/string.h>
31#include <iprt/asm.h>
32#include <iprt/ldr.h>
33#include <iprt/dir.h>
34#include <iprt/path.h>
35#include <iprt/param.h>
36
37#include "VBoxHDD-newInternal.h"
38
39
40#define VBOXHDDDISK_SIGNATURE 0x6f0e2a7d
41
42/** Buffer size used for merging images. */
43#define VD_MERGE_BUFFER_SIZE (1024 * 1024)
44
45/**
46 * VBox HDD Container image descriptor.
47 */
48typedef struct VDIMAGE
49{
50 /** Link to parent image descriptor, if any. */
51 struct VDIMAGE *pPrev;
52 /** Link to child image descriptor, if any. */
53 struct VDIMAGE *pNext;
54 /** Container base filename. (UTF-8) */
55 char *pszFilename;
56 /** Data managed by the backend which keeps the actual info. */
57 void *pvBackendData;
58 /** Image open flags (only those handled generically in this code and which
59 * the backends will never ever see). */
60 unsigned uOpenFlags;
61} VDIMAGE, *PVDIMAGE;
62
63/**
64 * uModified bit flags.
65 */
66#define VD_IMAGE_MODIFIED_FLAG RT_BIT(0)
67#define VD_IMAGE_MODIFIED_FIRST RT_BIT(1)
68#define VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE RT_BIT(2)
69
70
71/**
72 * VBox HDD Container main structure, private part.
73 */
74struct VBOXHDD
75{
76 /** Structure signature (VBOXHDDDISK_SIGNATURE). */
77 uint32_t u32Signature;
78
79 /** Number of opened images. */
80 unsigned cImages;
81
82 /** Base image. */
83 PVDIMAGE pBase;
84
85 /** Last opened image in the chain.
86 * The same as pBase if only one image is used. */
87 PVDIMAGE pLast;
88
89 /** Flags representing the modification state. */
90 unsigned uModified;
91
92 /** Cached size of this disk. */
93 uint64_t cbSize;
94 /** Cached PCHS geometry for this disk. */
95 PDMMEDIAGEOMETRY PCHSGeometry;
96 /** Cached LCHS geometry for this disk. */
97 PDMMEDIAGEOMETRY LCHSGeometry;
98
99 /** Error message processing callback. */
100 PFNVDERROR pfnError;
101 /** Opaque data for error callback. */
102 void *pvErrorUser;
103
104 /** Handle for the shared object / DLL. */
105 RTLDRMOD hPlugin;
106 /** Function pointers for the various backend methods. */
107 PCVBOXHDDBACKEND Backend;
108};
109
110
111extern VBOXHDDBACKEND g_VmdkBackend;
112#ifndef VBOX_OSE
113extern VBOXHDDBACKEND g_VhdBackend;
114#endif
115
116static PCVBOXHDDBACKEND aBackends[] =
117{
118 { &g_VmdkBackend },
119 { &g_VhdBackend},
120 { NULL }
121};
122
123
124/**
125 * internal: issue early error message.
126 */
127static int vdEarlyError(PFNVDERROR pfnError, void *pvErrorUser, int rc,
128 RT_SRC_POS_DECL, const char *pszFormat, ...)
129{
130 va_list va;
131 va_start(va, pszFormat);
132 if (pfnError)
133 pfnError(pvErrorUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
134 va_end(va);
135 return rc;
136}
137
138/**
139 * internal: issue error message.
140 */
141static int vdError(PVBOXHDD pDisk, int rc, RT_SRC_POS_DECL,
142 const char *pszFormat, ...)
143{
144 va_list va;
145 va_start(va, pszFormat);
146 if (pDisk->pfnError)
147 pDisk->pfnError(pDisk->pvErrorUser, rc, RT_SRC_POS_ARGS, pszFormat, va);
148 va_end(va);
149 return rc;
150}
151
152/**
153 * internal: add image structure to the end of images list.
154 */
155static void vdAddImageToList(PVBOXHDD pDisk, PVDIMAGE pImage)
156{
157 pImage->pPrev = NULL;
158 pImage->pNext = NULL;
159
160 if (pDisk->pBase)
161 {
162 Assert(pDisk->cImages > 0);
163 pImage->pPrev = pDisk->pLast;
164 pDisk->pLast->pNext = pImage;
165 pDisk->pLast = pImage;
166 }
167 else
168 {
169 Assert(pDisk->cImages == 0);
170 pDisk->pBase = pImage;
171 pDisk->pLast = pImage;
172 }
173
174 pDisk->cImages++;
175}
176
177/**
178 * internal: remove image structure from the images list.
179 */
180static void vdRemoveImageFromList(PVBOXHDD pDisk, PVDIMAGE pImage)
181{
182 Assert(pDisk->cImages > 0);
183
184 if (pImage->pPrev)
185 pImage->pPrev->pNext = pImage->pNext;
186 else
187 pDisk->pBase = pImage->pNext;
188
189 if (pImage->pNext)
190 pImage->pNext->pPrev = pImage->pPrev;
191 else
192 pDisk->pLast = pImage->pPrev;
193
194 pImage->pPrev = NULL;
195 pImage->pNext = NULL;
196
197 pDisk->cImages--;
198}
199
200/**
201 * internal: find image by index into the images list.
202 */
203static PVDIMAGE vdGetImageByNumber(PVBOXHDD pDisk, unsigned nImage)
204{
205 PVDIMAGE pImage = pDisk->pBase;
206 if (nImage == VD_LAST_IMAGE)
207 return pDisk->pLast;
208 while (pImage && nImage)
209 {
210 pImage = pImage->pNext;
211 nImage--;
212 }
213 return pImage;
214}
215
216/**
217 * internal: read the specified amount of data in whatever blocks the backend
218 * will give us.
219 */
220static int vdReadHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
221 void *pvBuf, size_t cbRead)
222{
223 int rc;
224 size_t cbThisRead;
225
226 /* Loop until all read. */
227 do
228 {
229 /* Search for image with allocated block. Do not attempt to read more
230 * than the previous reads marked as valid. Otherwise this would return
231 * stale data when different block sizes are used for the images. */
232 cbThisRead = cbRead;
233 rc = VINF_VDI_BLOCK_FREE;
234 for (PVDIMAGE pCurrImage = pImage;
235 pCurrImage != NULL && rc == VINF_VDI_BLOCK_FREE;
236 pCurrImage = pCurrImage->pPrev)
237 {
238 rc = pDisk->Backend->pfnRead(pCurrImage->pvBackendData, uOffset,
239 pvBuf, cbThisRead, &cbThisRead);
240 }
241
242 /* No image in the chain contains the data for the block. */
243 if (rc == VINF_VDI_BLOCK_FREE)
244 {
245 memset(pvBuf, '\0', cbThisRead);
246 rc = VINF_SUCCESS;
247 }
248
249 cbRead -= cbThisRead;
250 uOffset += cbThisRead;
251 pvBuf = (char *)pvBuf + cbThisRead;
252 } while (cbRead != 0 && VBOX_SUCCESS(rc));
253
254 return rc;
255}
256
257/**
258 * internal: mark the disk as not modified.
259 */
260static void vdResetModifiedFlag(PVBOXHDD pDisk)
261{
262 if (pDisk->uModified & VD_IMAGE_MODIFIED_FLAG)
263 {
264 /* generate new last-modified uuid */
265 if (!(pDisk->uModified | VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
266 {
267 RTUUID Uuid;
268
269 RTUuidCreate(&Uuid);
270 pDisk->Backend->pfnSetModificationUuid(pDisk->pLast->pvBackendData,
271 &Uuid);
272 }
273
274 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FLAG;
275 }
276}
277
278/**
279 * internal: mark the disk as modified.
280 */
281static void vdSetModifiedFlag(PVBOXHDD pDisk)
282{
283 pDisk->uModified |= VD_IMAGE_MODIFIED_FLAG;
284 if (pDisk->uModified & VD_IMAGE_MODIFIED_FIRST)
285 {
286 pDisk->uModified &= ~VD_IMAGE_MODIFIED_FIRST;
287
288 /* First modify, so create a UUID and ensure it's written to disk. */
289 vdResetModifiedFlag(pDisk);
290
291 if (!(pDisk->uModified | VD_IMAGE_MODIFIED_DISABLE_UUID_UPDATE))
292 pDisk->Backend->pfnFlush(pDisk->pLast->pvBackendData);
293 }
294}
295
296/**
297 * internal: write a complete block (only used for diff images), taking the
298 * remaining data from parent images. This implementation does not optimize
299 * anything (except that it tries to read only that portions from parent
300 * images that are really needed).
301 */
302static int vdWriteHelperStandard(PVBOXHDD pDisk, PVDIMAGE pImage,
303 uint64_t uOffset, size_t cbWrite,
304 size_t cbThisWrite, size_t cbPreRead,
305 size_t cbPostRead, const void *pvBuf,
306 void *pvTmp)
307{
308 int rc = VINF_SUCCESS;
309
310 /* Read the data that goes before the write to fill the block. */
311 if (cbPreRead)
312 {
313 rc = vdReadHelper(pDisk, pImage->pPrev, uOffset - cbPreRead, pvTmp,
314 cbPreRead);
315 if (VBOX_FAILURE(rc))
316 return rc;
317 }
318
319 /* Copy the data to the right place in the buffer. */
320 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
321
322 /* Read the data that goes after the write to fill the block. */
323 if (cbPostRead)
324 {
325 /* If we have data to be written, use that instead of reading
326 * data from the image. */
327 size_t cbWriteCopy;
328 if (cbWrite > cbThisWrite)
329 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
330 else
331 cbWriteCopy = 0;
332 /* Figure out how much we cannnot read from the image, because
333 * the last block to write might exceed the nominal size of the
334 * image for technical reasons. */
335 size_t cbFill;
336 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
337 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
338 else
339 cbFill = 0;
340 /* The rest must be read from the image. */
341 size_t cbReadImage = cbPostRead - cbWriteCopy - cbFill;
342
343 /* Now assemble the remaining data. */
344 if (cbWriteCopy)
345 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
346 (char *)pvBuf + cbThisWrite, cbWriteCopy);
347 if (cbReadImage)
348 rc = vdReadHelper(pDisk, pImage->pPrev,
349 uOffset + cbThisWrite + cbWriteCopy,
350 (char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy,
351 cbReadImage);
352 if (VBOX_FAILURE(rc))
353 return rc;
354 /* Zero out the remainder of this block. Will never be visible, as this
355 * is beyond the limit of the image. */
356 if (cbFill)
357 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
358 '\0', cbFill);
359 }
360
361 /* Write the full block to the virtual disk. */
362 rc = pDisk->Backend->pfnWrite(pImage->pvBackendData,
363 uOffset - cbPreRead, pvTmp,
364 cbPreRead + cbThisWrite + cbPostRead,
365 NULL,
366 &cbPreRead, &cbPostRead);
367 Assert(rc != VINF_VDI_BLOCK_FREE);
368 Assert(cbPreRead == 0);
369 Assert(cbPostRead == 0);
370
371 return rc;
372}
373
374/**
375 * internal: write a complete block (only used for diff images), taking the
376 * remaining data from parent images. This implementation optimized out writes
377 * that do not change the data relative to the state as of the parent images.
378 * All backends which support differential/growing images support this.
379 */
380static int vdWriteHelperOptimized(PVBOXHDD pDisk, PVDIMAGE pImage,
381 uint64_t uOffset, size_t cbWrite,
382 size_t cbThisWrite, size_t cbPreRead,
383 size_t cbPostRead, const void *pvBuf,
384 void *pvTmp)
385{
386 size_t cbFill = 0;
387 size_t cbWriteCopy = 0;
388 size_t cbReadImage = 0;
389 int rc;
390
391 if (cbPostRead)
392 {
393 /* Figure out how much we cannnot read from the image, because
394 * the last block to write might exceed the nominal size of the
395 * image for technical reasons. */
396 if (uOffset + cbThisWrite + cbPostRead > pDisk->cbSize)
397 cbFill = uOffset + cbThisWrite + cbPostRead - pDisk->cbSize;
398
399 /* If we have data to be written, use that instead of reading
400 * data from the image. */
401 if (cbWrite > cbThisWrite)
402 cbWriteCopy = RT_MIN(cbWrite - cbThisWrite, cbPostRead);
403
404 /* The rest must be read from the image. */
405 cbReadImage = cbPostRead - cbWriteCopy - cbFill;
406 }
407
408 /* Read the entire data of the block so that we can compare whether it will
409 * be modified by the write or not. */
410 rc = vdReadHelper(pDisk, pImage->pPrev, uOffset - cbPreRead, pvTmp,
411 cbPreRead + cbThisWrite + cbPostRead - cbFill);
412 if (VBOX_FAILURE(rc))
413 return rc;
414
415 /* Check if the write would modify anything in this block. */
416 if ( !memcmp((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite)
417 && (!cbWriteCopy || !memcmp((char *)pvTmp + cbPreRead + cbThisWrite,
418 (char *)pvBuf + cbThisWrite, cbWriteCopy)))
419 {
420 /* Block is completely unchanged, so no need to write anything. */
421 return VINF_SUCCESS;
422 }
423
424 /* Copy the data to the right place in the buffer. */
425 memcpy((char *)pvTmp + cbPreRead, pvBuf, cbThisWrite);
426
427 /* Handle the data that goes after the write to fill the block. */
428 if (cbPostRead)
429 {
430 /* Now assemble the remaining data. */
431 if (cbWriteCopy)
432 memcpy((char *)pvTmp + cbPreRead + cbThisWrite,
433 (char *)pvBuf + cbThisWrite, cbWriteCopy);
434 /* Zero out the remainder of this block. Will never be visible, as this
435 * is beyond the limit of the image. */
436 if (cbFill)
437 memset((char *)pvTmp + cbPreRead + cbThisWrite + cbWriteCopy + cbReadImage,
438 '\0', cbFill);
439 }
440
441 /* Write the full block to the virtual disk. */
442 rc = pDisk->Backend->pfnWrite(pImage->pvBackendData,
443 uOffset - cbPreRead, pvTmp,
444 cbPreRead + cbThisWrite + cbPostRead,
445 NULL,
446 &cbPreRead, &cbPostRead);
447 Assert(rc != VINF_VDI_BLOCK_FREE);
448 Assert(cbPreRead == 0);
449 Assert(cbPostRead == 0);
450
451 return rc;
452}
453
454/**
455 * internal: write buffer to the image, taking care of block boundaries and
456 * write optimizations.
457 */
458static int vdWriteHelper(PVBOXHDD pDisk, PVDIMAGE pImage, uint64_t uOffset,
459 const void *pvBuf, size_t cbWrite)
460{
461 int rc;
462 size_t cbThisWrite;
463 size_t cbPreRead, cbPostRead;
464
465 /* Loop until all written. */
466 do
467 {
468 /* Try to write the possibly partial block to the last opened image.
469 * This works when the block is already allocated in this image or
470 * if it is a full-block write, which automatically allocates a new
471 * block if needed. */
472 cbThisWrite = cbWrite;
473 rc = pDisk->Backend->pfnWrite(pImage->pvBackendData, uOffset, pvBuf,
474 cbThisWrite, &cbThisWrite, &cbPreRead,
475 &cbPostRead);
476 if (rc == VINF_VDI_BLOCK_FREE)
477 {
478 void *pvTmp = RTMemTmpAlloc(cbPreRead + cbThisWrite + cbPostRead);
479 AssertBreak(!pvTmp, rc = VERR_NO_MEMORY);
480
481 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME))
482 {
483 /* Optimized write, suppress writing to a so far unallocated
484 * block if the data is in fact not changed. */
485 rc = vdWriteHelperOptimized(pDisk, pImage, uOffset, cbWrite,
486 cbThisWrite, cbPreRead, cbPostRead,
487 pvBuf, pvTmp);
488 }
489 else
490 {
491 /* Normal write, not optimized in any way. The block will
492 * be written no matter what. This will usually (unless the
493 * backend has some further optimization enabled) cause the
494 * block to be allocated. */
495 rc = vdWriteHelperStandard(pDisk, pImage, uOffset, cbWrite,
496 cbThisWrite, cbPreRead, cbPostRead,
497 pvBuf, pvTmp);
498 }
499 RTMemTmpFree(pvTmp);
500 if (VBOX_FAILURE(rc))
501 break;
502 }
503
504 cbWrite -= cbThisWrite;
505 uOffset += cbThisWrite;
506 pvBuf = (char *)pvBuf + cbThisWrite;
507 } while (cbWrite != 0 && VBOX_SUCCESS(rc));
508
509 return rc;
510}
511
512
513/**
514 * Allocates and initializes an empty HDD container.
515 * No image files are opened.
516 *
517 * @returns VBox status code.
518 * @param pszBackend Name of the image file backend to use.
519 * @param pfnError Callback for setting extended error information.
520 * @param pvErrorUser Opaque parameter for pfnError.
521 * @param ppDisk Where to store the reference to HDD container.
522 */
523VBOXDDU_DECL(int) VDCreate(const char *pszBackend, PFNVDERROR pfnError,
524 void *pvErrorUser, PVBOXHDD *ppDisk)
525{
526 int rc = VINF_SUCCESS;
527 PCVBOXHDDBACKEND pBackend = NULL;
528 PVBOXHDD pDisk = NULL;
529 RTLDRMOD hPlugin = NULL;
530
531 LogFlowFunc(("pszBackend=\"%s\" pfnError=%#p pvErrorUser=%#p\n",
532 pszBackend, pfnError, pvErrorUser));
533 do
534 {
535 /* Check arguments. */
536 AssertMsgBreak(VALID_PTR(pszBackend) && *pszBackend,
537 ("pszBackend=%#p \"%s\"\n", pszBackend, pszBackend),
538 rc = VERR_INVALID_PARAMETER);
539 AssertMsgBreak(VALID_PTR(pfnError),
540 ("pfnError=%#p\n", pfnError),
541 rc = VERR_INVALID_PARAMETER);
542 AssertMsgBreak(VALID_PTR(ppDisk),
543 ("ppDisk=%#p\n", ppDisk),
544 rc = VERR_INVALID_PARAMETER);
545
546 /* Find backend. */
547 for (unsigned i = 0; aBackends[i] != NULL; i++)
548 {
549 if (!strcmp(pszBackend, aBackends[i]->pszBackendName))
550 {
551 pBackend = aBackends[i];
552 break;
553 }
554 }
555
556 /* If no static backend is found try loading a shared module with
557 * pszBackend as filename. */
558 if (!pBackend)
559 {
560 char *pszPluginName;
561
562 /* HDD Format Plugins have VBoxHDD as prefix, prepend it. */
563 RTStrAPrintf(&pszPluginName, "%s%s",
564 VBOX_HDDFORMAT_PLUGIN_PREFIX, pszBackend);
565 if (!pszPluginName)
566 {
567 rc = VERR_NO_MEMORY;
568 break;
569 }
570
571 /* Try to load the plugin (RTldrLoad takes care of the suffix). */
572 rc = RTLdrLoad(pszPluginName, &hPlugin);
573 if (VBOX_SUCCESS(rc))
574 {
575 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad;
576
577 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME,
578 (void**)&pfnHDDFormatLoad);
579 if (VBOX_FAILURE(rc) || !pfnHDDFormatLoad)
580 {
581 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Vrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pszPluginName, rc, pfnHDDFormatLoad));
582 if (VBOX_SUCCESS(rc))
583 rc = VERR_SYMBOL_NOT_FOUND;
584 break;
585 }
586
587 /* Get the function table. */
588 PVBOXHDDBACKEND pBE;
589 rc = pfnHDDFormatLoad(&pBE);
590 if (VBOX_FAILURE(rc))
591 break;
592 /* Check if the sizes match. If not this plugin is too old. */
593 if (pBE->cbSize != sizeof(VBOXHDDBACKEND))
594 {
595 rc = VERR_VDI_UNSUPPORTED_VERSION;
596 break;
597 }
598 pBackend = pBE;
599 }
600
601 RTStrFree(pszPluginName);
602 }
603
604 if (pBackend)
605 {
606 pDisk = (PVBOXHDD)RTMemAllocZ(sizeof(VBOXHDD));
607 if (pDisk)
608 {
609 pDisk->u32Signature = VBOXHDDDISK_SIGNATURE;
610 pDisk->cImages = 0;
611 pDisk->pBase = NULL;
612 pDisk->pLast = NULL;
613 pDisk->cbSize = 0;
614 pDisk->PCHSGeometry.cCylinders = 0;
615 pDisk->PCHSGeometry.cHeads = 0;
616 pDisk->PCHSGeometry.cSectors = 0;
617 pDisk->LCHSGeometry.cCylinders = 0;
618 pDisk->LCHSGeometry.cHeads = 0;
619 pDisk->LCHSGeometry.cSectors = 0;
620 pDisk->pfnError = pfnError;
621 pDisk->pvErrorUser = pvErrorUser;
622 pDisk->Backend = pBackend;
623 pDisk->hPlugin = hPlugin;
624 *ppDisk = pDisk;
625 }
626 else
627 {
628 rc = VERR_NO_MEMORY;
629 break;
630 }
631 }
632 else
633 rc = vdEarlyError(pfnError, pvErrorUser, VERR_INVALID_PARAMETER,
634 RT_SRC_POS, "VD: unknown backend name '%s'",
635 pszBackend);
636 } while (0);
637
638 if (VBOX_FAILURE(rc) && hPlugin)
639 RTLdrClose(hPlugin);
640
641 LogFlowFunc(("returns %Vrc (pDisk=%#p)\n", rc, pDisk));
642 return rc;
643}
644
645/**
646 * Destroys HDD container.
647 * If container has opened image files they will be closed.
648 *
649 * @param pDisk Pointer to HDD container.
650 */
651VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk)
652{
653 LogFlowFunc(("pDisk=%#p\n", pDisk));
654 do
655 {
656 /* sanity check */
657 AssertBreak(VALID_PTR(pDisk), );
658 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
659
660 if (pDisk)
661 {
662 VDCloseAll(pDisk);
663 if (pDisk->hPlugin != NIL_RTLDRMOD)
664 {
665 RTLdrClose(pDisk->hPlugin);
666 pDisk->hPlugin = NIL_RTLDRMOD;
667 }
668 RTMemFree(pDisk);
669 }
670 } while (0);
671 LogFlowFunc(("returns\n"));
672}
673
674/**
675 * Try to get the backend name which can use this image.
676 *
677 * @returns VBox status code.
678 * VINF_SUCCESS if a plugin was found.
679 * ppszFormat contains the string which can be used as backend name.
680 * VERR_NOT_SUPPORTED if no plugin was found.
681 * @param pszFilename Name of the image file for which the backend is queried.
682 * @param ppszFormat Receives pointer of the UTF-8 string which contains the format name.
683 * The returned pointer must be freed using RTStrFree().
684 */
685VBOXDDU_DECL(int) VDGetFormat(const char *pszFilename, char **ppszFormat)
686{
687 PRTDIR pPluginDir = NULL;
688 int rc = VERR_NOT_SUPPORTED;
689 int rcCheck = VINF_SUCCESS;
690 bool fPluginFound = false;
691
692 LogFlowFunc(("pszFilename=\"%s\"\n", pszFilename));
693 do
694 {
695 /* Check arguments. */
696 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
697 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
698 rc = VERR_INVALID_PARAMETER);
699 AssertMsgBreak(VALID_PTR(ppszFormat),
700 ("ppszFormat=%#p\n", ppszFormat),
701 rc = VERR_INVALID_PARAMETER);
702
703 /* First check if static backends support this file format. */
704 for (unsigned i = 0; aBackends[i] != NULL; i++)
705 {
706 if (aBackends[i]->pfnCheckIfValid)
707 {
708 rc = aBackends[i]->pfnCheckIfValid(pszFilename);
709 if (VBOX_SUCCESS(rc))
710 {
711 fPluginFound = true;
712 /* Copy the name into the new string. */
713 char *pszFormat = RTStrDup(aBackends[i]->pszBackendName);
714 if (!pszFormat)
715 {
716 rc = VERR_NO_MEMORY;
717 break;
718 }
719 *ppszFormat = pszFormat;
720 break;
721 }
722 }
723 }
724
725 /* Then check if plugin backends support this file format. */
726 char szPath[RTPATH_MAX];
727 rc = RTPathSharedLibs(szPath, sizeof(szPath));
728 if (VBOX_FAILURE(rc))
729 break;
730
731 /* To get all entries with VBoxHDD as prefix. */
732 char *pszPluginFilter;
733 rc = RTStrAPrintf(&pszPluginFilter, "%s/%s*", szPath,
734 VBOX_HDDFORMAT_PLUGIN_PREFIX);
735 if (VBOX_FAILURE(rc))
736 {
737 rc = VERR_NO_MEMORY;
738 break;
739 }
740
741 /* The plugins are in the same directory as the other shared libs. */
742 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT);
743 if (VBOX_FAILURE(rc))
744 break;
745
746 PRTDIRENTRY pPluginDirEntry = NULL;
747 unsigned cbPluginDirEntry;
748 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(sizeof(RTDIRENTRY));
749 if (!pPluginDir)
750 {
751 rc = VERR_NO_MEMORY;
752 break;
753 }
754
755 while ((rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry)) != VERR_NO_MORE_FILES)
756 {
757 RTLDRMOD hPlugin = NIL_RTLDRMOD;
758 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL;
759 PVBOXHDDBACKEND pBackend = NULL;
760
761 if (rc == VERR_BUFFER_OVERFLOW)
762 {
763 /* allocate new buffer. */
764 RTMemFree(pPluginDirEntry);
765 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(cbPluginDirEntry);
766 /* Retry. */
767 rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry);
768 if (VBOX_FAILURE(rc))
769 break;
770 }
771 else if (VBOX_FAILURE(rc))
772 break;
773
774 /* We got the new entry. */
775 if (pPluginDirEntry->enmType != RTDIRENTRYTYPE_FILE)
776 continue;
777
778 rc = RTLdrLoad(pPluginDirEntry->szName, &hPlugin);
779 if (VBOX_SUCCESS(rc))
780 {
781 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad);
782 if (VBOX_FAILURE(rc) || !pfnHDDFormatLoad)
783 {
784 LogFunc(("error resolving the entry point %s in plugin %s, rc=%Vrc, pfnHDDFormat=%#p\n", VBOX_HDDFORMAT_LOAD_NAME, pPluginDirEntry->szName, rc, pfnHDDFormatLoad));
785 if (VBOX_SUCCESS(rc))
786 rc = VERR_SYMBOL_NOT_FOUND;
787 }
788
789 if (VBOX_SUCCESS(rc))
790 {
791 /* Get the function table. */
792 rc = pfnHDDFormatLoad(&pBackend);
793 if (VBOX_SUCCESS(rc) && pBackend->cbSize == sizeof(VBOXHDDBACKEND))
794 {
795
796 /* Check if the plugin can handle this file. */
797 rc = pBackend->pfnCheckIfValid(pszFilename);
798 if (VBOX_SUCCESS(rc))
799 {
800 fPluginFound = true;
801 rc = VINF_SUCCESS;
802
803 /* Report the format name. */
804 RTPathStripExt(pPluginDirEntry->szName);
805 char *pszFormat = NULL;
806 AssertBreak(strlen(pszFormat) >= VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH,
807 rc = VERR_INVALID_NAME);
808 pszFormat = RTStrDup(pPluginDirEntry->szName + VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH);
809 if (!pszFormat)
810 rc = VERR_NO_MEMORY;
811 *ppszFormat = pszFormat;
812 }
813 }
814 }
815 else
816 pBackend = NULL;
817 }
818 RTLdrClose(hPlugin);
819 /*
820 * We take the first plugin which can handle this file.
821 */
822 if (fPluginFound)
823 break;
824 }
825 RTStrFree(pszPluginFilter);
826 if (pPluginDirEntry)
827 RTMemFree(pPluginDirEntry);
828 if (pPluginDir)
829 RTDirClose(pPluginDir);
830 } while (0);
831
832 LogFlowFunc(("returns %Vrc *ppszFormat=\"%s\"\n", rc, *ppszFormat));
833 return rc;
834}
835
836/**
837 * Opens an image file.
838 *
839 * The first opened image file in HDD container must have a base image type,
840 * others (next opened images) must be a differencing or undo images.
841 * Linkage is checked for differencing image to be in consistence with the previously opened image.
842 * When another differencing image is opened and the last image was opened in read/write access
843 * mode, then the last image is reopened in read-only with deny write sharing mode. This allows
844 * other processes to use images in read-only mode too.
845 *
846 * Note that the image is opened in read-only mode if a read/write open is not possible.
847 * Use VDIsReadOnly to check open mode.
848 *
849 * @returns VBox status code.
850 * @param pDisk Pointer to HDD container.
851 * @param pszFilename Name of the image file to open.
852 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
853 */
854VBOXDDU_DECL(int) VDOpen(PVBOXHDD pDisk, const char *pszFilename,
855 unsigned uOpenFlags)
856{
857 int rc = VINF_SUCCESS;
858 PVDIMAGE pImage = NULL;
859
860 LogFlowFunc(("pDisk=%#p pszFilename=\"%s\" uOpenFlags=%#x\n",
861 pszFilename, uOpenFlags));
862 do
863 {
864 /* sanity check */
865 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
866 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
867
868 /* Check arguments. */
869 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
870 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
871 rc = VERR_INVALID_PARAMETER);
872 AssertMsgBreak((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
873 ("uOpenFlags=%#x\n", uOpenFlags),
874 rc = VERR_INVALID_PARAMETER);
875
876 /* Force readonly for images without base/diff consistency checking. */
877 if (uOpenFlags & VD_OPEN_FLAGS_INFO)
878 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
879
880 /* Set up image descriptor. */
881 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
882 if (!pImage)
883 {
884 rc = VERR_NO_MEMORY;
885 break;
886 }
887 pImage->pszFilename = RTStrDup(pszFilename);
888 if (!pImage->pszFilename)
889 {
890 rc = VERR_NO_MEMORY;
891 break;
892 }
893
894 pImage->uOpenFlags = uOpenFlags & VD_OPEN_FLAGS_HONOR_SAME;
895 rc = pDisk->Backend->pfnOpen(pImage->pszFilename,
896 uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME,
897 pDisk->pfnError, pDisk->pvErrorUser,
898 &pImage->pvBackendData);
899 /* If the open in read-write mode failed, retry in read-only mode. */
900 if (VBOX_FAILURE(rc))
901 {
902 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY)
903 && (rc == VERR_ACCESS_DENIED
904 || rc == VERR_PERMISSION_DENIED
905 || rc == VERR_WRITE_PROTECT
906 || rc == VERR_SHARING_VIOLATION
907 || rc == VERR_FILE_LOCK_FAILED))
908 rc = pDisk->Backend->pfnOpen(pImage->pszFilename,
909 (uOpenFlags & ~VD_OPEN_FLAGS_HONOR_SAME)
910 | VD_OPEN_FLAGS_READONLY,
911 pDisk->pfnError, pDisk->pvErrorUser,
912 &pImage->pvBackendData);
913 if (VBOX_FAILURE(rc))
914 {
915 rc = vdError(pDisk, rc, RT_SRC_POS,
916 N_("VD: error opening image file '%s'"), pszFilename);
917 break;
918 }
919 }
920
921 VDIMAGETYPE enmImageType;
922 rc = pDisk->Backend->pfnGetImageType(pImage->pvBackendData,
923 &enmImageType);
924 /* Check image type. As the image itself has no idea whether it's a
925 * base image or not, this info is derived here. Image 0 can be fixed
926 * or normal, all others must be normal images. */
927 if ( VBOX_SUCCESS(rc)
928 && !(uOpenFlags & VD_OPEN_FLAGS_INFO)
929 && pDisk->cImages != 0
930 && enmImageType != VD_IMAGE_TYPE_NORMAL)
931 {
932 rc = VERR_VDI_INVALID_TYPE;
933 break;
934 }
935
936 /** @todo optionally check UUIDs */
937
938 int rc2;
939
940 /* Cache disk information. */
941 pDisk->cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
942
943 /* Cache PCHS geometry. */
944 rc2 = pDisk->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
945 &pDisk->PCHSGeometry);
946 if (VBOX_FAILURE(rc2))
947 {
948 pDisk->PCHSGeometry.cCylinders = 0;
949 pDisk->PCHSGeometry.cHeads = 0;
950 pDisk->PCHSGeometry.cSectors = 0;
951 }
952 else
953 {
954 /* Make sure the PCHS geometry is properly clipped. */
955 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
956 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
957 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
958 }
959
960 /* Cache LCHS geometry. */
961 rc2 = pDisk->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
962 &pDisk->LCHSGeometry);
963 if (VBOX_FAILURE(rc2))
964 {
965 pDisk->LCHSGeometry.cCylinders = 0;
966 pDisk->LCHSGeometry.cHeads = 0;
967 pDisk->LCHSGeometry.cSectors = 0;
968 }
969 else
970 {
971 /* Make sure the LCHS geometry is properly clipped. */
972 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
973 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
974 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
975 }
976
977 if (pDisk->cImages != 0)
978 {
979 /* Switch previous image to read-only mode. */
980 unsigned uOpenFlagsPrevImg;
981 uOpenFlagsPrevImg = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
982 if (!(uOpenFlagsPrevImg & VD_OPEN_FLAGS_READONLY))
983 {
984 uOpenFlagsPrevImg |= VD_OPEN_FLAGS_READONLY;
985 rc = pDisk->Backend->pfnSetOpenFlags(pDisk->pLast->pvBackendData, uOpenFlagsPrevImg);
986 }
987 }
988
989 if (VBOX_SUCCESS(rc))
990 {
991 /* Image successfully opened, make it the last image. */
992 vdAddImageToList(pDisk, pImage);
993 }
994 else
995 {
996 /* Error detected, but image opened. Close image. */
997 int rc2;
998 rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, false);
999 AssertRC(rc2);
1000 pImage->pvBackendData = NULL;
1001 }
1002 } while (0);
1003
1004 if (VBOX_FAILURE(rc))
1005 {
1006 if (pImage)
1007 {
1008 if (pImage->pszFilename)
1009 RTStrFree(pImage->pszFilename);
1010 RTMemFree(pImage);
1011 }
1012 }
1013
1014 LogFlowFunc(("returns %Vrc\n", rc));
1015 return rc;
1016}
1017
1018/**
1019 * Creates and opens a new base image file.
1020 *
1021 * @returns VBox status code.
1022 * @param pDisk Pointer to HDD container.
1023 * @param pszFilename Name of the image file to create.
1024 * @param enmType Image type, only base image types are acceptable.
1025 * @param cbSize Image size in bytes.
1026 * @param uImageFlags Flags specifying special image features.
1027 * @param pszComment Pointer to image comment. NULL is ok.
1028 * @param pPCHSGeometry Pointer to physical disk geometry <= (16383,16,63). Not NULL.
1029 * @param pLCHSGeometry Pointer to logical disk geometry <= (1024,255,63). Not NULL.
1030 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1031 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1032 * @param pvUser User argument for the progress callback.
1033 */
1034VBOXDDU_DECL(int) VDCreateBase(PVBOXHDD pDisk, const char *pszFilename,
1035 VDIMAGETYPE enmType, uint64_t cbSize,
1036 unsigned uImageFlags, const char *pszComment,
1037 PCPDMMEDIAGEOMETRY pPCHSGeometry,
1038 PCPDMMEDIAGEOMETRY pLCHSGeometry,
1039 unsigned uOpenFlags, PFNVMPROGRESS pfnProgress,
1040 void *pvUser)
1041{
1042 int rc = VINF_SUCCESS;
1043 PVDIMAGE pImage = NULL;
1044
1045 LogFlowFunc(("pszFilename=\"%s\" enmType=%#x cbSize=%llu uImageFlags=%#x pszComment=\"%s\" PCHS=%u/%u/%u LCHS=%u/%u/%u uOpenFlags=%#x pfnProgress=%#p pvUser=%#p\n",
1046 pszFilename, enmType, cbSize, uImageFlags, pszComment,
1047 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1048 pPCHSGeometry->cSectors, pLCHSGeometry->cCylinders,
1049 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors, uOpenFlags,
1050 pfnProgress, pvUser));
1051 do
1052 {
1053 /* sanity check */
1054 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1055 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1056
1057 /* Check arguments. */
1058 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
1059 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1060 rc = VERR_INVALID_PARAMETER);
1061 AssertMsgBreak(enmType == VD_IMAGE_TYPE_NORMAL || enmType == VD_IMAGE_TYPE_FIXED,
1062 ("enmType=%#x\n", enmType),
1063 rc = VERR_INVALID_PARAMETER);
1064 AssertMsgBreak(cbSize,
1065 ("cbSize=%llu\n", cbSize),
1066 rc = VERR_INVALID_PARAMETER);
1067 AssertMsgBreak((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1068 ("uImageFlags=%#x\n", uImageFlags),
1069 rc = VERR_INVALID_PARAMETER);
1070 AssertMsgBreak( VALID_PTR(pPCHSGeometry)
1071 && pPCHSGeometry->cCylinders <= 16383
1072 && pPCHSGeometry->cCylinders != 0
1073 && pPCHSGeometry->cHeads <= 16
1074 && pPCHSGeometry->cHeads != 0
1075 && pPCHSGeometry->cSectors <= 63
1076 && pPCHSGeometry->cSectors != 0,
1077 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
1078 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1079 pPCHSGeometry->cSectors),
1080 rc = VERR_INVALID_PARAMETER);
1081 AssertMsgBreak( VALID_PTR(pLCHSGeometry)
1082 && pLCHSGeometry->cCylinders <= 16383
1083 && pLCHSGeometry->cCylinders != 0
1084 && pLCHSGeometry->cHeads <= 16
1085 && pLCHSGeometry->cHeads != 0
1086 && pLCHSGeometry->cSectors <= 63
1087 && pLCHSGeometry->cSectors != 0,
1088 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
1089 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
1090 pLCHSGeometry->cSectors),
1091 rc = VERR_INVALID_PARAMETER);
1092 AssertMsgBreak((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1093 ("uOpenFlags=%#x\n", uOpenFlags),
1094 rc = VERR_INVALID_PARAMETER);
1095
1096 /* Check state. */
1097 if (pDisk->cImages != 0)
1098 AssertMsgFailedBreak(("Create base image cannot be done with other images open\n"),
1099 rc = VERR_VDI_INVALID_STATE);
1100
1101 /* Set up image descriptor. */
1102 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1103 if (!pImage)
1104 {
1105 rc = VERR_NO_MEMORY;
1106 break;
1107 }
1108 pImage->pszFilename = RTStrDup(pszFilename);
1109 if (!pImage->pszFilename)
1110 {
1111 rc = VERR_NO_MEMORY;
1112 break;
1113 }
1114
1115 rc = pDisk->Backend->pfnCreate(pImage->pszFilename, enmType, cbSize,
1116 uImageFlags, pszComment, pPCHSGeometry,
1117 pLCHSGeometry, uOpenFlags, pfnProgress,
1118 pvUser, 0, 99, pDisk->pfnError,
1119 pDisk->pvErrorUser,
1120 &pImage->pvBackendData);
1121
1122 if (VBOX_SUCCESS(rc))
1123 {
1124 /** @todo optionally check UUIDs */
1125
1126 int rc2;
1127
1128 /* Cache disk information. */
1129 pDisk->cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
1130
1131 /* Cache PCHS geometry. */
1132 rc2 = pDisk->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1133 &pDisk->PCHSGeometry);
1134 if (VBOX_FAILURE(rc2))
1135 {
1136 pDisk->PCHSGeometry.cCylinders = 0;
1137 pDisk->PCHSGeometry.cHeads = 0;
1138 pDisk->PCHSGeometry.cSectors = 0;
1139 }
1140 else
1141 {
1142 /* Make sure the CHS geometry is properly clipped. */
1143 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1144 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1145 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1146 }
1147
1148 /* Cache LCHS geometry. */
1149 rc2 = pDisk->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1150 &pDisk->LCHSGeometry);
1151 if (VBOX_FAILURE(rc2))
1152 {
1153 pDisk->LCHSGeometry.cCylinders = 0;
1154 pDisk->LCHSGeometry.cHeads = 0;
1155 pDisk->LCHSGeometry.cSectors = 0;
1156 }
1157 else
1158 {
1159 /* Make sure the CHS geometry is properly clipped. */
1160 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1161 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1162 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1163 }
1164 }
1165
1166 if (VBOX_SUCCESS(rc))
1167 {
1168 /* Image successfully opened, make it the last image. */
1169 vdAddImageToList(pDisk, pImage);
1170 }
1171 else
1172 {
1173 /* Error detected, but image opened. Close and delete image. */
1174 int rc2;
1175 rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, true);
1176 AssertRC(rc2);
1177 pImage->pvBackendData = NULL;
1178 }
1179 } while (0);
1180
1181 if (VBOX_FAILURE(rc))
1182 {
1183 if (pImage)
1184 {
1185 if (pImage->pszFilename)
1186 RTStrFree(pImage->pszFilename);
1187 RTMemFree(pImage);
1188 }
1189 }
1190
1191 if (VBOX_SUCCESS(rc) && pfnProgress)
1192 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1193
1194 LogFlowFunc(("returns %Vrc\n", rc));
1195 return rc;
1196}
1197
1198/**
1199 * Creates and opens a new differencing image file in HDD container.
1200 * See comments for VDOpen function about differencing images.
1201 *
1202 * @returns VBox status code.
1203 * @param pDisk Pointer to HDD container.
1204 * @param pszFilename Name of the differencing image file to create.
1205 * @param uImageFlags Flags specifying special image features.
1206 * @param pszComment Pointer to image comment. NULL is ok.
1207 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
1208 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1209 * @param pvUser User argument for the progress callback.
1210 */
1211VBOXDDU_DECL(int) VDCreateDiff(PVBOXHDD pDisk, const char *pszFilename,
1212 unsigned uImageFlags, const char *pszComment,
1213 unsigned uOpenFlags, PFNVMPROGRESS pfnProgress,
1214 void *pvUser)
1215{
1216 int rc = VINF_SUCCESS;
1217 PVDIMAGE pImage = NULL;
1218
1219 LogFlowFunc(("pDisk=%#p pszFilename=\"%s\" uImageFlags=%#x pszComment=\"%s\" uOpenFlags=%#x pfnProgress=%#p pvUser=%#p\n",
1220 pDisk, pszFilename, uImageFlags, pszComment, uOpenFlags,
1221 pfnProgress, pvUser));
1222 do
1223 {
1224 /* sanity check */
1225 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1226 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1227
1228 /* Check arguments. */
1229 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
1230 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
1231 rc = VERR_INVALID_PARAMETER);
1232 AssertMsgBreak((uImageFlags & ~VD_IMAGE_FLAGS_MASK) == 0,
1233 ("uImageFlags=%#x\n", uImageFlags),
1234 rc = VERR_INVALID_PARAMETER);
1235 AssertMsgBreak((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
1236 ("uOpenFlags=%#x\n", uOpenFlags),
1237 rc = VERR_INVALID_PARAMETER);
1238
1239 /* Check state. */
1240 if (pDisk->cImages == 0)
1241 AssertMsgFailedBreak(("Create diff image cannot be done without other images open\n"),
1242 rc = VERR_VDI_INVALID_STATE);
1243
1244 /* Set up image descriptor. */
1245 pImage = (PVDIMAGE)RTMemAllocZ(sizeof(VDIMAGE));
1246 if (!pImage)
1247 {
1248 rc = VERR_NO_MEMORY;
1249 break;
1250 }
1251 pImage->pszFilename = RTStrDup(pszFilename);
1252 if (!pImage->pszFilename)
1253 {
1254 rc = VERR_NO_MEMORY;
1255 break;
1256 }
1257
1258 rc = pDisk->Backend->pfnCreate(pImage->pszFilename,
1259 VD_IMAGE_TYPE_NORMAL, pDisk->cbSize,
1260 uImageFlags, pszComment,
1261 &pDisk->PCHSGeometry,
1262 &pDisk->LCHSGeometry, uOpenFlags,
1263 pfnProgress, pvUser, 0, 99,
1264 pDisk->pfnError, pDisk->pvErrorUser,
1265 &pImage->pvBackendData);
1266
1267 if (VBOX_SUCCESS(rc))
1268 {
1269 /** @todo optionally check UUIDs */
1270 }
1271
1272 if (VBOX_SUCCESS(rc))
1273 {
1274 /* Image successfully opened, make it the last image. */
1275 vdAddImageToList(pDisk, pImage);
1276 }
1277 else
1278 {
1279 /* Error detected, but image opened. Close and delete image. */
1280 int rc2;
1281 rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, true);
1282 AssertRC(rc2);
1283 pImage->pvBackendData = NULL;
1284 }
1285 } while (0);
1286
1287 if (VBOX_FAILURE(rc))
1288 {
1289 if (pImage)
1290 {
1291 if (pImage->pszFilename)
1292 RTStrFree(pImage->pszFilename);
1293 RTMemFree(pImage);
1294 }
1295 }
1296
1297 if (VBOX_SUCCESS(rc) && pfnProgress)
1298 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1299
1300 LogFlowFunc(("returns %Vrc\n", rc));
1301 return rc;
1302}
1303
1304/**
1305 * Merges two images (not necessarily with direct parent/child relationship).
1306 * As a side effect the source image and potentially the other images which
1307 * are also merged to the destination are deleted from both the disk and the
1308 * images in the HDD container.
1309 *
1310 * @returns VBox status code.
1311 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1312 * @param pDisk Pointer to HDD container.
1313 * @param nImageFrom Name of the image file to merge from.
1314 * @param nImageTo Name of the image file to merge to.
1315 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1316 * @param pvUser User argument for the progress callback.
1317 */
1318VBOXDDU_DECL(int) VDMerge(PVBOXHDD pDisk, unsigned nImageFrom,
1319 unsigned nImageTo, PFNVMPROGRESS pfnProgress,
1320 void *pvUser)
1321{
1322 int rc = VINF_SUCCESS;
1323 void *pvBuf = NULL;
1324
1325 LogFlowFunc(("pDisk=%#p nImageFrom=%u nImageTo=%u pfnProgress=%#p pvUser=%#p\n",
1326 pDisk, nImageFrom, nImageTo, pfnProgress, pvUser));
1327 do
1328 {
1329 /* sanity check */
1330 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1331 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1332
1333 PVDIMAGE pImageFrom = vdGetImageByNumber(pDisk, nImageFrom);
1334 PVDIMAGE pImageTo = vdGetImageByNumber(pDisk, nImageTo);
1335 if (!pImageFrom || !pImageTo)
1336 {
1337 rc = VERR_VDI_IMAGE_NOT_FOUND;
1338 break;
1339 }
1340 AssertBreak(pImageFrom != pImageTo, rc = VERR_INVALID_PARAMETER);
1341
1342 /* Check if destination image is writable. */
1343 unsigned uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pImageTo->pvBackendData);
1344 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
1345 {
1346 rc = VERR_VDI_IMAGE_READ_ONLY;
1347 break;
1348 }
1349
1350 /* Get size of destination image. */
1351 uint64_t cbSize = pDisk->Backend->pfnGetSize(pImageTo->pvBackendData);
1352
1353 /* Allocate tmp buffer. */
1354 pvBuf = RTMemTmpAlloc(VD_MERGE_BUFFER_SIZE);
1355 if (!pvBuf)
1356 {
1357 rc = VERR_NO_MEMORY;
1358 break;
1359 }
1360
1361 /* Merging is done directly on the images itself. This potentially
1362 * causes trouble if the disk is full in the middle of operation. */
1363 /** @todo write alternative implementation which works with temporary
1364 * images (which is safer, but requires even more space). Also has the
1365 * drawback that merging into a raw disk parent simply isn't possible
1366 * this way (but in that case disk full isn't really a problem). */
1367 if (nImageFrom < nImageTo)
1368 {
1369 /* Merge parent state into child. This means writing all not
1370 * allocated blocks in the destination image which are allocated in
1371 * the images to be merged. */
1372 uint64_t uOffset = 0;
1373 uint64_t cbRemaining = cbSize;
1374 do
1375 {
1376 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1377 rc = pDisk->Backend->pfnRead(pImageTo->pvBackendData, uOffset,
1378 pvBuf, cbThisRead, &cbThisRead);
1379 if (VBOX_FAILURE(rc))
1380 break;
1381 if (rc == VINF_VDI_BLOCK_FREE)
1382 {
1383 /* Search for image with allocated block. Do not attempt to
1384 * read more than the previous reads marked as valid.
1385 * Otherwise this would return stale data when different
1386 * block sizes are used for the images. */
1387 for (PVDIMAGE pCurrImage = pImageTo->pPrev;
1388 pCurrImage != NULL && pCurrImage != pImageFrom->pPrev && rc == VINF_VDI_BLOCK_FREE;
1389 pCurrImage = pCurrImage->pPrev)
1390 {
1391 rc = pDisk->Backend->pfnRead(pCurrImage->pvBackendData,
1392 uOffset, pvBuf,
1393 cbThisRead, &cbThisRead);
1394 }
1395 if (VBOX_FAILURE(rc))
1396 break;
1397
1398 if (rc != VINF_VDI_BLOCK_FREE)
1399 {
1400 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1401 cbThisRead);
1402 if (VBOX_FAILURE(rc))
1403 break;
1404 }
1405 }
1406
1407 uOffset += cbThisRead;
1408 cbRemaining -= cbThisRead;
1409 } while (uOffset < cbSize);
1410 }
1411 else
1412 {
1413 /* Merge child state into parent. This means writing all blocks
1414 * which are allocated in the image up to the source image to the
1415 * destination image. */
1416 uint64_t uOffset = 0;
1417 uint64_t cbRemaining = cbSize;
1418 do
1419 {
1420 size_t cbThisRead = RT_MIN(VD_MERGE_BUFFER_SIZE, cbRemaining);
1421 /* Search for image with allocated block. Do not attempt to
1422 * read more than the previous reads marked as valid. Otherwise
1423 * this would return stale data when different block sizes are
1424 * used for the images. */
1425 for (PVDIMAGE pCurrImage = pImageFrom;
1426 pCurrImage != NULL && pCurrImage != pImageTo && rc == VINF_VDI_BLOCK_FREE;
1427 pCurrImage = pCurrImage->pPrev)
1428 {
1429 rc = pDisk->Backend->pfnRead(pCurrImage->pvBackendData,
1430 uOffset, pvBuf,
1431 cbThisRead, &cbThisRead);
1432 }
1433 if (VBOX_FAILURE(rc))
1434 break;
1435
1436 if (rc != VINF_VDI_BLOCK_FREE)
1437 {
1438 rc = vdWriteHelper(pDisk, pImageTo, uOffset, pvBuf,
1439 cbThisRead);
1440 if (VBOX_FAILURE(rc))
1441 break;
1442 }
1443
1444 uOffset += cbThisRead;
1445 cbRemaining -= cbThisRead;
1446 } while (uOffset < cbSize);
1447 }
1448
1449 /* Update parent UUID so that image chain is consistent. */
1450 RTUUID Uuid;
1451 if (nImageFrom < nImageTo)
1452 {
1453 if (pImageTo->pPrev)
1454 {
1455 rc = pDisk->Backend->pfnGetUuid(pImageTo->pPrev->pvBackendData,
1456 &Uuid);
1457 AssertRC(rc);
1458 }
1459 else
1460 RTUuidClear(&Uuid);
1461 rc = pDisk->Backend->pfnSetParentUuid(pImageTo->pvBackendData,
1462 &Uuid);
1463 AssertRC(rc);
1464 }
1465 else
1466 {
1467 if (pImageFrom->pNext)
1468 {
1469 rc = pDisk->Backend->pfnGetUuid(pImageTo->pvBackendData,
1470 &Uuid);
1471 AssertRC(rc);
1472 rc = pDisk->Backend->pfnSetParentUuid(pImageFrom->pNext,
1473 &Uuid);
1474 AssertRC(rc);
1475 }
1476 }
1477
1478 /* Delete the no longer needed images. */
1479 PVDIMAGE pImg = pImageFrom, pTmp;
1480 while (pImg != pImageTo)
1481 {
1482 if (nImageFrom < nImageTo)
1483 pTmp = pImg->pNext;
1484 else
1485 pTmp = pImg->pPrev;
1486 vdRemoveImageFromList(pDisk, pImg);
1487 pDisk->Backend->pfnClose(pImg->pvBackendData, true);
1488 pImg = pTmp;
1489 }
1490 } while (0);
1491
1492 if (pvBuf)
1493 RTMemTmpFree(pvBuf);
1494
1495 if (VBOX_SUCCESS(rc) && pfnProgress)
1496 pfnProgress(NULL /* WARNING! pVM=NULL */, 100, pvUser);
1497
1498 LogFlowFunc(("returns %Vrc\n", rc));
1499 return rc;
1500}
1501
1502/**
1503 * Copies an image from one HDD container to another.
1504 * The copy is opened in the target HDD container.
1505 * It is possible to convert between different image formats, because the
1506 * backend for the destination HDD container may be different from the
1507 * source container.
1508 * If both the source and destination reference the same HDD container,
1509 * then the image is moved (by copying/deleting or renaming) to the new location.
1510 * The source container is unchanged if the move operation fails, otherwise
1511 * the image at the new location is opened in the same way as the old one was.
1512 *
1513 * @returns VBox status code.
1514 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1515 * @param pDiskFrom Pointer to source HDD container.
1516 * @param nImage Image number, counts from 0. 0 is always base image of container.
1517 * @param pDiskTo Pointer to destination HDD container.
1518 * @param pszFilename New name of the image (may be NULL if pDiskFrom == pDiskTo).
1519 * @param fMoveByRename If true, attempt to perform a move by renaming (if successful the new size is ignored).
1520 * @param cbSize New image size (0 means leave unchanged).
1521 * @param pfnProgress Progress callback. Optional. NULL if not to be used.
1522 * @param pvUser User argument for the progress callback.
1523 */
1524VBOXDDU_DECL(int) VDCopy(PVBOXHDD pDiskFrom, unsigned nImage, PVBOXHDD pDiskTo,
1525 const char *pszFilename, bool fMoveByRename,
1526 uint64_t cbSize, PFNVMPROGRESS pfnProgress,
1527 void *pvUser)
1528{
1529 return VERR_NOT_IMPLEMENTED;
1530}
1531
1532/**
1533 * Closes the last opened image file in HDD container.
1534 * If previous image file was opened in read-only mode (that is normal) and closing image
1535 * was opened in read-write mode (the whole disk was in read-write mode) - the previous image
1536 * will be reopened in read/write mode.
1537 *
1538 * @returns VBox status code.
1539 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1540 * @param pDisk Pointer to HDD container.
1541 * @param fDelete If true, delete the image from the host disk.
1542 */
1543VBOXDDU_DECL(int) VDClose(PVBOXHDD pDisk, bool fDelete)
1544{
1545 int rc = VINF_SUCCESS;;
1546
1547 LogFlowFunc(("fDelete=%d\n", fDelete));
1548 do
1549 {
1550 /* sanity check */
1551 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1552 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1553
1554 PVDIMAGE pImage = pDisk->pLast;
1555 if (RT_UNLIKELY(!pImage))
1556 {
1557 Assert(pImage);
1558 rc = VERR_VDI_NOT_OPENED;
1559 break;
1560 }
1561 unsigned uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pImage->pvBackendData);
1562 /* Remove image from list of opened images. */
1563 vdRemoveImageFromList(pDisk, pImage);
1564 /* Close (and optionally delete) image. */
1565 rc = pDisk->Backend->pfnClose(pImage->pvBackendData, fDelete);
1566 /* Free remaining resources related to the image. */
1567 RTStrFree(pImage->pszFilename);
1568 RTMemFree(pImage);
1569
1570 pImage = pDisk->pLast;
1571 if (!pImage)
1572 break;
1573
1574 /* If disk was previously in read/write mode, make sure it will stay
1575 * like this (if possible) after closing this image. Set the open flags
1576 * accordingly. */
1577 if (!(uOpenFlags & VD_OPEN_FLAGS_READONLY))
1578 {
1579 uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pImage->pvBackendData);
1580 uOpenFlags &= ~ VD_OPEN_FLAGS_READONLY;
1581 rc = pDisk->Backend->pfnSetOpenFlags(pImage->pvBackendData, uOpenFlags);
1582 }
1583
1584 int rc2;
1585
1586 /* Cache disk information. */
1587 pDisk->cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
1588
1589 /* Cache PCHS geometry. */
1590 rc2 = pDisk->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1591 &pDisk->PCHSGeometry);
1592 if (VBOX_FAILURE(rc2))
1593 {
1594 pDisk->PCHSGeometry.cCylinders = 0;
1595 pDisk->PCHSGeometry.cHeads = 0;
1596 pDisk->PCHSGeometry.cSectors = 0;
1597 }
1598 else
1599 {
1600 /* Make sure the PCHS geometry is properly clipped. */
1601 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 16383);
1602 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 16);
1603 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
1604 }
1605
1606 /* Cache LCHS geometry. */
1607 rc2 = pDisk->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
1608 &pDisk->LCHSGeometry);
1609 if (VBOX_FAILURE(rc2))
1610 {
1611 pDisk->LCHSGeometry.cCylinders = 0;
1612 pDisk->LCHSGeometry.cHeads = 0;
1613 pDisk->LCHSGeometry.cSectors = 0;
1614 }
1615 else
1616 {
1617 /* Make sure the LCHS geometry is properly clipped. */
1618 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
1619 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
1620 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
1621 }
1622 } while (0);
1623
1624 LogFlowFunc(("returns %Vrc\n", rc));
1625 return rc;
1626}
1627
1628/**
1629 * Closes all opened image files in HDD container.
1630 *
1631 * @returns VBox status code.
1632 * @param pDisk Pointer to HDD container.
1633 */
1634VBOXDDU_DECL(int) VDCloseAll(PVBOXHDD pDisk)
1635{
1636 int rc = VINF_SUCCESS;
1637
1638 LogFlowFunc(("pDisk=%#p\n", pDisk));
1639 do
1640 {
1641 /* sanity check */
1642 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1643 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1644
1645 PVDIMAGE pImage = pDisk->pLast;
1646 while (pImage)
1647 {
1648 PVDIMAGE pPrev = pImage->pPrev;
1649 /* Remove image from list of opened images. */
1650 vdRemoveImageFromList(pDisk, pImage);
1651 /* Close image. */
1652 int rc2 = pDisk->Backend->pfnClose(pImage->pvBackendData, false);
1653 if (VBOX_FAILURE(rc2) && VBOX_SUCCESS(rc))
1654 rc = rc2;
1655 /* Free remaining resources related to the image. */
1656 RTStrFree(pImage->pszFilename);
1657 RTMemFree(pImage);
1658 pImage = pPrev;
1659 }
1660 Assert(pDisk->pLast == NULL);
1661 } while (0);
1662
1663 LogFlowFunc(("returns %Vrc\n", rc));
1664 return rc;
1665}
1666
1667/**
1668 * Read data from virtual HDD.
1669 *
1670 * @returns VBox status code.
1671 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1672 * @param pDisk Pointer to HDD container.
1673 * @param uOffset Offset of first reading byte from start of disk.
1674 * @param pvBuf Pointer to buffer for reading data.
1675 * @param cbRead Number of bytes to read.
1676 */
1677VBOXDDU_DECL(int) VDRead(PVBOXHDD pDisk, uint64_t uOffset, void *pvBuf,
1678 size_t cbRead)
1679{
1680 int rc;
1681
1682 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbRead=%llu\n",
1683 pDisk, uOffset, pvBuf, cbRead));
1684 do
1685 {
1686 /* sanity check */
1687 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1688 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1689
1690 /* Check arguments. */
1691 AssertMsgBreak(VALID_PTR(pvBuf),
1692 ("pvBuf=%#p\n", pvBuf),
1693 rc = VERR_INVALID_PARAMETER);
1694 AssertMsgBreak(cbRead,
1695 ("cbRead=%llu\n", cbRead),
1696 rc = VERR_INVALID_PARAMETER);
1697 AssertMsgBreak(uOffset + cbRead > pDisk->cbSize,
1698 ("uOffset=%llu cbRead=%llu pDisk->cbSize=%llu\n",
1699 uOffset, cbRead, pDisk->cbSize),
1700 rc = VERR_INVALID_PARAMETER);
1701
1702 PVDIMAGE pImage = pDisk->pLast;
1703 AssertBreak(pImage, rc = VERR_VDI_NOT_OPENED);
1704
1705 rc = vdReadHelper(pDisk, pImage, uOffset, pvBuf, cbRead);
1706 } while (0);
1707
1708 LogFlowFunc(("returns %Vrc\n", rc));
1709 return rc;
1710}
1711
1712/**
1713 * Write data to virtual HDD.
1714 *
1715 * @returns VBox status code.
1716 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1717 * @param pDisk Pointer to HDD container.
1718 * @param uOffset Offset of first reading byte from start of disk.
1719 * @param pvBuf Pointer to buffer for writing data.
1720 * @param cbWrite Number of bytes to write.
1721 */
1722VBOXDDU_DECL(int) VDWrite(PVBOXHDD pDisk, uint64_t uOffset, const void *pvBuf, size_t cbWrite)
1723{
1724 int rc = VINF_SUCCESS;
1725
1726 LogFlowFunc(("pDisk=%#p uOffset=%llu pvBuf=%p cbWrite=%llu\n",
1727 pDisk, uOffset, pvBuf, cbWrite));
1728 do
1729 {
1730 /* sanity check */
1731 AssertBreak(VALID_PTR(pDisk), VERR_INVALID_PARAMETER);
1732 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1733
1734 /* Check arguments. */
1735 AssertMsgBreak(VALID_PTR(pvBuf),
1736 ("pvBuf=%#p\n", pvBuf),
1737 rc = VERR_INVALID_PARAMETER);
1738 AssertMsgBreak(cbWrite,
1739 ("cbWrite=%llu\n", cbWrite),
1740 rc = VERR_INVALID_PARAMETER);
1741 AssertMsgBreak(uOffset + cbWrite > pDisk->cbSize,
1742 ("uOffset=%llu cbWrite=%llu pDisk->cbSize=%llu\n",
1743 uOffset, cbWrite, pDisk->cbSize),
1744 rc = VERR_INVALID_PARAMETER);
1745
1746 PVDIMAGE pImage = pDisk->pLast;
1747 AssertBreak(pImage, rc = VERR_VDI_NOT_OPENED);
1748
1749 vdSetModifiedFlag(pDisk);
1750 rc = vdWriteHelper(pDisk, pImage, uOffset, pvBuf, cbWrite);
1751 } while (0);
1752
1753 LogFlowFunc(("returns %Vrc\n", rc));
1754 return rc;
1755}
1756
1757/**
1758 * Make sure the on disk representation of a virtual HDD is up to date.
1759 *
1760 * @returns VBox status code.
1761 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
1762 * @param pDisk Pointer to HDD container.
1763 */
1764VBOXDDU_DECL(int) VDFlush(PVBOXHDD pDisk)
1765{
1766 int rc = VINF_SUCCESS;
1767
1768 LogFlowFunc(("pDisk=%#p\n", pDisk));
1769 do
1770 {
1771 /* sanity check */
1772 AssertBreak(VALID_PTR(pDisk), VERR_INVALID_PARAMETER);
1773 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1774
1775 PVDIMAGE pImage = pDisk->pLast;
1776 AssertBreak(pImage, rc = VERR_VDI_NOT_OPENED);
1777
1778 vdResetModifiedFlag(pDisk);
1779 rc = pDisk->Backend->pfnFlush(pImage->pvBackendData);
1780 } while (0);
1781
1782 LogFlowFunc(("returns %Vrc\n", rc));
1783 return rc;
1784}
1785
1786/**
1787 * Get number of opened images in HDD container.
1788 *
1789 * @returns Number of opened images for HDD container. 0 if no images have been opened.
1790 * @param pDisk Pointer to HDD container.
1791 */
1792VBOXDDU_DECL(unsigned) VDGetCount(PVBOXHDD pDisk)
1793{
1794 unsigned cImages;
1795
1796 LogFlowFunc(("pDisk=%#p\n", pDisk));
1797 do
1798 {
1799 /* sanity check */
1800 AssertBreak(VALID_PTR(pDisk), cImages = 0);
1801 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1802
1803 cImages = pDisk->cImages;
1804 } while (0);
1805
1806 LogFlowFunc(("returns %u\n", cImages));
1807 return cImages;
1808}
1809
1810/**
1811 * Get read/write mode of HDD container.
1812 *
1813 * @returns Virtual disk ReadOnly status.
1814 * @returns true if no image is opened in HDD container.
1815 * @param pDisk Pointer to HDD container.
1816 */
1817VBOXDDU_DECL(bool) VDIsReadOnly(PVBOXHDD pDisk)
1818{
1819 bool fReadOnly;
1820
1821 LogFlowFunc(("pDisk=%#p\n", pDisk));
1822 do
1823 {
1824 /* sanity check */
1825 AssertBreak(VALID_PTR(pDisk), fReadOnly = false);
1826 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1827
1828 PVDIMAGE pImage = pDisk->pLast;
1829 AssertBreak(pImage, fReadOnly = true);
1830
1831 unsigned uOpenFlags;
1832 uOpenFlags = pDisk->Backend->pfnGetOpenFlags(pDisk->pLast->pvBackendData);
1833 fReadOnly = !!(uOpenFlags & VD_OPEN_FLAGS_READONLY);
1834 } while (0);
1835
1836 LogFlowFunc(("returns %d\n", fReadOnly));
1837 return fReadOnly;
1838}
1839
1840/**
1841 * Get total capacity of an image in HDD container.
1842 *
1843 * @returns Virtual disk size in bytes.
1844 * @returns 0 if no image with specified number was not opened.
1845 * @param pDisk Pointer to HDD container.
1846 * @param nImage Image number, counds from 0. 0 is always base image of container.
1847 */
1848VBOXDDU_DECL(uint64_t) VDGetSize(PVBOXHDD pDisk, unsigned nImage)
1849{
1850 uint64_t cbSize;
1851
1852 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
1853 do
1854 {
1855 /* sanity check */
1856 AssertBreak(VALID_PTR(pDisk), cbSize = 0);
1857 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1858
1859 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1860 AssertBreak(pImage, cbSize = 0);
1861 cbSize = pDisk->Backend->pfnGetSize(pImage->pvBackendData);
1862 } while (0);
1863
1864 LogFlowFunc(("returns %llu\n", cbSize));
1865 return cbSize;
1866}
1867
1868/**
1869 * Get total file size of an image in HDD container.
1870 *
1871 * @returns Virtual disk size in bytes.
1872 * @returns 0 if no image is opened in HDD container.
1873 * @param pDisk Pointer to HDD container.
1874 * @param nImage Image number, counts from 0. 0 is always base image of container.
1875 */
1876VBOXDDU_DECL(uint64_t) VDGetFileSize(PVBOXHDD pDisk, unsigned nImage)
1877{
1878 uint64_t cbSize;
1879
1880 LogFlowFunc(("pDisk=%#p nImage=%u\n", pDisk, nImage));
1881 do
1882 {
1883 /* sanity check */
1884 AssertBreak(VALID_PTR(pDisk), cbSize = 0);
1885 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1886
1887 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1888 AssertBreak(pImage, cbSize = 0);
1889 cbSize = pDisk->Backend->pfnGetFileSize(pImage->pvBackendData);
1890 } while (0);
1891
1892 LogFlowFunc(("returns %llu\n", cbSize));
1893 return cbSize;
1894}
1895
1896/**
1897 * Get virtual disk PCHS geometry stored in HDD container.
1898 *
1899 * @returns VBox status code.
1900 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1901 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
1902 * @param pDisk Pointer to HDD container.
1903 * @param nImage Image number, counts from 0. 0 is always base image of container.
1904 * @param pPCHSGeometry Where to store PCHS geometry. Not NULL.
1905 */
1906VBOXDDU_DECL(int) VDGetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
1907 PPDMMEDIAGEOMETRY pPCHSGeometry)
1908{
1909 int rc = VINF_SUCCESS;
1910
1911 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p\n",
1912 pDisk, nImage, pPCHSGeometry));
1913 do
1914 {
1915 /* sanity check */
1916 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
1917 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1918
1919 /* Check arguments. */
1920 AssertMsgBreak(VALID_PTR(pPCHSGeometry),
1921 ("pPCHSGeometry=%#p\n", pPCHSGeometry),
1922 rc = VERR_INVALID_PARAMETER);
1923
1924 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1925 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
1926
1927 if (pImage == pDisk->pLast)
1928 {
1929 /* Use cached information if possible. */
1930 if (pDisk->PCHSGeometry.cCylinders != 0)
1931 *pPCHSGeometry = pDisk->PCHSGeometry;
1932 else
1933 rc = VERR_VDI_GEOMETRY_NOT_SET;
1934 }
1935 else
1936 rc = pDisk->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
1937 pPCHSGeometry);
1938 } while (0);
1939
1940 LogFlowFunc(("%s: %Vrc (PCHS=%u/%u/%u)\n", rc,
1941 pDisk->PCHSGeometry.cCylinders, pDisk->PCHSGeometry.cHeads,
1942 pDisk->PCHSGeometry.cSectors));
1943 return rc;
1944}
1945
1946/**
1947 * Store virtual disk PCHS geometry in HDD container.
1948 *
1949 * Note that in case of unrecoverable error all images in HDD container will be closed.
1950 *
1951 * @returns VBox status code.
1952 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
1953 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
1954 * @param pDisk Pointer to HDD container.
1955 * @param nImage Image number, counts from 0. 0 is always base image of container.
1956 * @param pPCHSGeometry Where to load PCHS geometry from. Not NULL.
1957 */
1958VBOXDDU_DECL(int) VDSetPCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
1959 PCPDMMEDIAGEOMETRY pPCHSGeometry)
1960{
1961 int rc = VINF_SUCCESS;
1962
1963 LogFlowFunc(("pDisk=%#p nImage=%u pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
1964 pDisk, nImage, pPCHSGeometry, pPCHSGeometry->cCylinders,
1965 pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
1966 do
1967 {
1968 /* sanity check */
1969 AssertBreak(VALID_PTR(pDisk), VERR_INVALID_PARAMETER);
1970 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
1971
1972 /* Check arguments. */
1973 AssertMsgBreak( VALID_PTR(pPCHSGeometry)
1974 && pPCHSGeometry->cCylinders <= 16383
1975 && pPCHSGeometry->cCylinders != 0
1976 && pPCHSGeometry->cHeads <= 16
1977 && pPCHSGeometry->cHeads != 0
1978 && pPCHSGeometry->cSectors <= 63
1979 && pPCHSGeometry->cSectors != 0,
1980 ("pPCHSGeometry=%#p PCHS=%u/%u/%u\n", pPCHSGeometry,
1981 pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads,
1982 pPCHSGeometry->cSectors),
1983 rc = VERR_INVALID_PARAMETER);
1984
1985 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
1986 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
1987
1988 if (pImage == pDisk->pLast)
1989 {
1990 if ( pPCHSGeometry->cCylinders != pDisk->PCHSGeometry.cCylinders
1991 || pPCHSGeometry->cHeads != pDisk->PCHSGeometry.cHeads
1992 || pPCHSGeometry->cSectors != pDisk->PCHSGeometry.cSectors)
1993 {
1994 /* Only update geometry if it is changed. Avoids similar checks
1995 * in every backend. Most of the time the new geometry is set
1996 * to the previous values, so no need to go through the hassle
1997 * of updating an image which could be opened in read-only mode
1998 * right now. */
1999 rc = pDisk->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2000 pPCHSGeometry);
2001
2002 /* Cache new geometry values in any case. */
2003 int rc2 = pDisk->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2004 &pDisk->PCHSGeometry);
2005 if (VBOX_FAILURE(rc2))
2006 {
2007 pDisk->PCHSGeometry.cCylinders = 0;
2008 pDisk->PCHSGeometry.cHeads = 0;
2009 pDisk->PCHSGeometry.cSectors = 0;
2010 }
2011 else
2012 {
2013 /* Make sure the CHS geometry is properly clipped. */
2014 pDisk->PCHSGeometry.cCylinders = RT_MIN(pDisk->PCHSGeometry.cCylinders, 1024);
2015 pDisk->PCHSGeometry.cHeads = RT_MIN(pDisk->PCHSGeometry.cHeads, 255);
2016 pDisk->PCHSGeometry.cSectors = RT_MIN(pDisk->PCHSGeometry.cSectors, 63);
2017 }
2018 }
2019 }
2020 else
2021 {
2022 PDMMEDIAGEOMETRY PCHS;
2023 rc = pDisk->Backend->pfnGetPCHSGeometry(pImage->pvBackendData,
2024 &PCHS);
2025 if ( VBOX_FAILURE(rc)
2026 || pPCHSGeometry->cCylinders != PCHS.cCylinders
2027 || pPCHSGeometry->cHeads != PCHS.cHeads
2028 || pPCHSGeometry->cSectors != PCHS.cSectors)
2029 {
2030 /* Only update geometry if it is changed. Avoids similar checks
2031 * in every backend. Most of the time the new geometry is set
2032 * to the previous values, so no need to go through the hassle
2033 * of updating an image which could be opened in read-only mode
2034 * right now. */
2035 rc = pDisk->Backend->pfnSetPCHSGeometry(pImage->pvBackendData,
2036 pPCHSGeometry);
2037 }
2038 }
2039 } while (0);
2040
2041 LogFlowFunc(("returns %Vrc\n", rc));
2042 return rc;
2043}
2044
2045/**
2046 * Get virtual disk LCHS geometry stored in HDD container.
2047 *
2048 * @returns VBox status code.
2049 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2050 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2051 * @param pDisk Pointer to HDD container.
2052 * @param nImage Image number, counts from 0. 0 is always base image of container.
2053 * @param pLCHSGeometry Where to store LCHS geometry. Not NULL.
2054 */
2055VBOXDDU_DECL(int) VDGetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2056 PPDMMEDIAGEOMETRY pLCHSGeometry)
2057{
2058 int rc = VINF_SUCCESS;
2059
2060 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p\n",
2061 pDisk, nImage, pLCHSGeometry));
2062 do
2063 {
2064 /* sanity check */
2065 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2066 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2067
2068 /* Check arguments. */
2069 AssertMsgBreak(VALID_PTR(pLCHSGeometry),
2070 ("pLCHSGeometry=%#p\n", pLCHSGeometry),
2071 rc = VERR_INVALID_PARAMETER);
2072
2073 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2074 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2075
2076 if (pImage == pDisk->pLast)
2077 {
2078 /* Use cached information if possible. */
2079 if (pDisk->LCHSGeometry.cCylinders != 0)
2080 *pLCHSGeometry = pDisk->LCHSGeometry;
2081 else
2082 rc = VERR_VDI_GEOMETRY_NOT_SET;
2083 }
2084 else
2085 rc = pDisk->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2086 pLCHSGeometry);
2087 } while (0);
2088
2089 LogFlowFunc(("%s: %Vrc (LCHS=%u/%u/%u)\n", rc,
2090 pDisk->LCHSGeometry.cCylinders, pDisk->LCHSGeometry.cHeads,
2091 pDisk->LCHSGeometry.cSectors));
2092 return rc;
2093}
2094
2095/**
2096 * Store virtual disk LCHS geometry in HDD container.
2097 *
2098 * Note that in case of unrecoverable error all images in HDD container will be closed.
2099 *
2100 * @returns VBox status code.
2101 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2102 * @returns VERR_VDI_GEOMETRY_NOT_SET if no geometry present in the HDD container.
2103 * @param pDisk Pointer to HDD container.
2104 * @param nImage Image number, counts from 0. 0 is always base image of container.
2105 * @param pLCHSGeometry Where to load LCHS geometry from. Not NULL.
2106 */
2107VBOXDDU_DECL(int) VDSetLCHSGeometry(PVBOXHDD pDisk, unsigned nImage,
2108 PCPDMMEDIAGEOMETRY pLCHSGeometry)
2109{
2110 int rc = VINF_SUCCESS;
2111
2112 LogFlowFunc(("pDisk=%#p nImage=%u pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
2113 pDisk, nImage, pLCHSGeometry, pLCHSGeometry->cCylinders,
2114 pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
2115 do
2116 {
2117 /* sanity check */
2118 AssertBreak(VALID_PTR(pDisk), VERR_INVALID_PARAMETER);
2119 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2120
2121 /* Check arguments. */
2122 AssertMsgBreak( VALID_PTR(pLCHSGeometry)
2123 && pLCHSGeometry->cCylinders <= 1024
2124 && pLCHSGeometry->cCylinders != 0
2125 && pLCHSGeometry->cHeads <= 255
2126 && pLCHSGeometry->cHeads != 0
2127 && pLCHSGeometry->cSectors <= 63
2128 && pLCHSGeometry->cSectors != 0,
2129 ("pLCHSGeometry=%#p LCHS=%u/%u/%u\n", pLCHSGeometry,
2130 pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads,
2131 pLCHSGeometry->cSectors),
2132 rc = VERR_INVALID_PARAMETER);
2133
2134 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2135 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2136
2137 if (pImage == pDisk->pLast)
2138 {
2139 if ( pLCHSGeometry->cCylinders != pDisk->LCHSGeometry.cCylinders
2140 || pLCHSGeometry->cHeads != pDisk->LCHSGeometry.cHeads
2141 || pLCHSGeometry->cSectors != pDisk->LCHSGeometry.cSectors)
2142 {
2143 /* Only update geometry if it is changed. Avoids similar checks
2144 * in every backend. Most of the time the new geometry is set
2145 * to the previous values, so no need to go through the hassle
2146 * of updating an image which could be opened in read-only mode
2147 * right now. */
2148 rc = pDisk->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2149 pLCHSGeometry);
2150
2151 /* Cache new geometry values in any case. */
2152 int rc2 = pDisk->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2153 &pDisk->LCHSGeometry);
2154 if (VBOX_FAILURE(rc2))
2155 {
2156 pDisk->LCHSGeometry.cCylinders = 0;
2157 pDisk->LCHSGeometry.cHeads = 0;
2158 pDisk->LCHSGeometry.cSectors = 0;
2159 }
2160 else
2161 {
2162 /* Make sure the CHS geometry is properly clipped. */
2163 pDisk->LCHSGeometry.cCylinders = RT_MIN(pDisk->LCHSGeometry.cCylinders, 1024);
2164 pDisk->LCHSGeometry.cHeads = RT_MIN(pDisk->LCHSGeometry.cHeads, 255);
2165 pDisk->LCHSGeometry.cSectors = RT_MIN(pDisk->LCHSGeometry.cSectors, 63);
2166 }
2167 }
2168 }
2169 else
2170 {
2171 PDMMEDIAGEOMETRY LCHS;
2172 rc = pDisk->Backend->pfnGetLCHSGeometry(pImage->pvBackendData,
2173 &LCHS);
2174 if ( VBOX_FAILURE(rc)
2175 || pLCHSGeometry->cCylinders != LCHS.cCylinders
2176 || pLCHSGeometry->cHeads != LCHS.cHeads
2177 || pLCHSGeometry->cSectors != LCHS.cSectors)
2178 {
2179 /* Only update geometry if it is changed. Avoids similar checks
2180 * in every backend. Most of the time the new geometry is set
2181 * to the previous values, so no need to go through the hassle
2182 * of updating an image which could be opened in read-only mode
2183 * right now. */
2184 rc = pDisk->Backend->pfnSetLCHSGeometry(pImage->pvBackendData,
2185 pLCHSGeometry);
2186 }
2187 }
2188 } while (0);
2189
2190 LogFlowFunc(("returns %Vrc\n", rc));
2191 return rc;
2192}
2193
2194/**
2195 * Get version of image in HDD container.
2196 *
2197 * @returns VBox status code.
2198 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2199 * @param pDisk Pointer to HDD container.
2200 * @param nImage Image number, counts from 0. 0 is always base image of container.
2201 * @param puVersion Where to store the image version.
2202 */
2203VBOXDDU_DECL(int) VDGetVersion(PVBOXHDD pDisk, unsigned nImage,
2204 unsigned *puVersion)
2205{
2206 int rc = VINF_SUCCESS;
2207
2208 LogFlowFunc(("pDisk=%#p nImage=%u puVersion=%#p\n",
2209 pDisk, nImage, puVersion));
2210 do
2211 {
2212 /* sanity check */
2213 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2214 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2215
2216 /* Check arguments. */
2217 AssertMsgBreak(VALID_PTR(puVersion),
2218 ("puVersion=%#p\n", puVersion),
2219 rc = VERR_INVALID_PARAMETER);
2220
2221 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2222 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2223
2224 *puVersion = pDisk->Backend->pfnGetVersion(pImage->pvBackendData);
2225 } while (0);
2226
2227 LogFlowFunc(("returns %Vrc uVersion=%#x\n", rc, *puVersion));
2228 return rc;
2229}
2230
2231/**
2232 * Get type of image in HDD container.
2233 *
2234 * @returns VBox status code.
2235 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2236 * @param pDisk Pointer to HDD container.
2237 * @param nImage Image number, counts from 0. 0 is always base image of container.
2238 * @param penmType Where to store the image type.
2239 */
2240VBOXDDU_DECL(int) VDGetImageType(PVBOXHDD pDisk, unsigned nImage,
2241 PVDIMAGETYPE penmType)
2242{
2243 int rc;
2244
2245 LogFlowFunc(("pDisk=%#p nImage=%u penmType=%#p\n",
2246 pDisk, nImage, penmType));
2247 do
2248 {
2249 /* sanity check */
2250 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2251 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2252
2253 /* Check arguments. */
2254 AssertMsgBreak(VALID_PTR(penmType),
2255 ("penmType=%#p\n", penmType),
2256 rc = VERR_INVALID_PARAMETER);
2257
2258 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2259 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2260
2261 rc = pDisk->Backend->pfnGetImageType(pImage->pvBackendData,
2262 penmType);
2263 } while (0);
2264
2265 LogFlowFunc(("returns %Vrc uenmType=%u\n", rc, *penmType));
2266 return rc;
2267}
2268
2269/**
2270 * Get flags of image in HDD container.
2271 *
2272 * @returns VBox status code.
2273 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2274 * @param pDisk Pointer to HDD container.
2275 * @param nImage Image number, counts from 0. 0 is always base image of container.
2276 * @param puImageFlags Where to store the image flags.
2277 */
2278VBOXDDU_DECL(int) VDGetImageFlags(PVBOXHDD pDisk, unsigned nImage,
2279 unsigned *puImageFlags)
2280{
2281 int rc = VINF_SUCCESS;
2282
2283 LogFlowFunc(("pDisk=%#p nImage=%u puImageFlags=%#p\n",
2284 pDisk, nImage, puImageFlags));
2285 do
2286 {
2287 /* sanity check */
2288 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2289 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2290
2291 /* Check arguments. */
2292 AssertMsgBreak(VALID_PTR(puImageFlags),
2293 ("puImageFlags=%#p\n", puImageFlags),
2294 rc = VERR_INVALID_PARAMETER);
2295
2296 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2297 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2298
2299 *puImageFlags = pDisk->Backend->pfnGetImageFlags(pImage->pvBackendData);
2300 } while (0);
2301
2302 LogFlowFunc(("returns %Vrc uImageFlags=%#x\n", rc, *puImageFlags));
2303 return rc;
2304}
2305
2306/**
2307 * Get open flags of image in HDD container.
2308 *
2309 * @returns VBox status code.
2310 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2311 * @param pDisk Pointer to HDD container.
2312 * @param nImage Image number, counts from 0. 0 is always base image of container.
2313 * @param puOpenFlags Where to store the image open flags.
2314 */
2315VBOXDDU_DECL(int) VDGetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2316 unsigned *puOpenFlags)
2317{
2318 int rc = VINF_SUCCESS;
2319
2320 LogFlowFunc(("pDisk=%#p nImage=%u puOpenFlags=%#p\n",
2321 pDisk, nImage, puOpenFlags));
2322 do
2323 {
2324 /* sanity check */
2325 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2326 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2327
2328 /* Check arguments. */
2329 AssertMsgBreak(VALID_PTR(puOpenFlags),
2330 ("puOpenFlags=%#p\n", puOpenFlags),
2331 rc = VERR_INVALID_PARAMETER);
2332
2333 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2334 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2335
2336 *puOpenFlags = pDisk->Backend->pfnGetOpenFlags(pImage->pvBackendData);
2337 } while (0);
2338
2339 LogFlowFunc(("returns %Vrc uOpenFlags=%#x\n", rc, *puOpenFlags));
2340 return rc;
2341}
2342
2343/**
2344 * Set open flags of image in HDD container.
2345 * This operation may cause file locking changes and/or files being reopened.
2346 * Note that in case of unrecoverable error all images in HDD container will be closed.
2347 *
2348 * @returns VBox status code.
2349 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2350 * @param pDisk Pointer to HDD container.
2351 * @param nImage Image number, counts from 0. 0 is always base image of container.
2352 * @param uOpenFlags Image file open mode, see VD_OPEN_FLAGS_* constants.
2353 */
2354VBOXDDU_DECL(int) VDSetOpenFlags(PVBOXHDD pDisk, unsigned nImage,
2355 unsigned uOpenFlags)
2356{
2357 int rc;
2358
2359 LogFlowFunc(("pDisk=%#p uOpenFlags=%#u\n", pDisk, uOpenFlags));
2360 do
2361 {
2362 /* sanity check */
2363 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2364 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2365
2366 /* Check arguments. */
2367 AssertMsgBreak((uOpenFlags & ~VD_OPEN_FLAGS_MASK) == 0,
2368 ("uOpenFlags=%#x\n", uOpenFlags),
2369 rc = VERR_INVALID_PARAMETER);
2370
2371 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2372 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2373
2374 rc = pDisk->Backend->pfnSetOpenFlags(pImage->pvBackendData,
2375 uOpenFlags);
2376 } while (0);
2377
2378 LogFlowFunc(("returns %Vrc\n", rc));
2379 return rc;
2380}
2381
2382/**
2383 * Get base filename of image in HDD container. Some image formats use
2384 * other filenames as well, so don't use this for anything but informational
2385 * purposes.
2386 *
2387 * @returns VBox status code.
2388 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2389 * @returns VERR_BUFFER_OVERFLOW if pszFilename buffer too small to hold filename.
2390 * @param pDisk Pointer to HDD container.
2391 * @param nImage Image number, counts from 0. 0 is always base image of container.
2392 * @param pszFilename Where to store the image file name.
2393 * @param cbFilename Size of buffer pszFilename points to.
2394 */
2395VBOXDDU_DECL(int) VDGetFilename(PVBOXHDD pDisk, unsigned nImage,
2396 char *pszFilename, unsigned cbFilename)
2397{
2398 int rc;
2399
2400 LogFlowFunc(("pDisk=%#p nImage=%u pszFilename=%#p cbFilename=%u\n",
2401 pDisk, nImage, pszFilename, cbFilename));
2402 do
2403 {
2404 /* sanity check */
2405 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2406 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2407
2408 /* Check arguments. */
2409 AssertMsgBreak(VALID_PTR(pszFilename) && *pszFilename,
2410 ("pszFilename=%#p \"%s\"\n", pszFilename, pszFilename),
2411 rc = VERR_INVALID_PARAMETER);
2412 AssertMsgBreak(cbFilename,
2413 ("cbFilename=%u\n", cbFilename),
2414 rc = VERR_INVALID_PARAMETER);
2415
2416 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2417 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2418
2419 size_t cb = strlen(pImage->pszFilename);
2420 if (cb <= cbFilename)
2421 {
2422 strcpy(pszFilename, pImage->pszFilename);
2423 rc = VINF_SUCCESS;
2424 }
2425 else
2426 {
2427 strncpy(pszFilename, pImage->pszFilename, cbFilename - 1);
2428 pszFilename[cbFilename - 1] = '\0';
2429 rc = VERR_BUFFER_OVERFLOW;
2430 }
2431 } while (0);
2432
2433 LogFlowFunc(("returns %Vrc, pszFilename=\"%s\"\n", rc, pszFilename));
2434 return rc;
2435}
2436
2437/**
2438 * Get the comment line of image in HDD container.
2439 *
2440 * @returns VBox status code.
2441 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2442 * @returns VERR_BUFFER_OVERFLOW if pszComment buffer too small to hold comment text.
2443 * @param pDisk Pointer to HDD container.
2444 * @param nImage Image number, counts from 0. 0 is always base image of container.
2445 * @param pszComment Where to store the comment string of image. NULL is ok.
2446 * @param cbComment The size of pszComment buffer. 0 is ok.
2447 */
2448VBOXDDU_DECL(int) VDGetComment(PVBOXHDD pDisk, unsigned nImage,
2449 char *pszComment, unsigned cbComment)
2450{
2451 int rc;
2452
2453 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p cbComment=%u\n",
2454 pDisk, nImage, pszComment, cbComment));
2455 do
2456 {
2457 /* sanity check */
2458 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2459 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2460
2461 /* Check arguments. */
2462 AssertMsgBreak(VALID_PTR(pszComment),
2463 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
2464 rc = VERR_INVALID_PARAMETER);
2465 AssertMsgBreak(cbComment,
2466 ("cbComment=%u\n", cbComment),
2467 rc = VERR_INVALID_PARAMETER);
2468
2469 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2470 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2471
2472 rc = pDisk->Backend->pfnGetComment(pImage->pvBackendData, pszComment,
2473 cbComment);
2474 } while (0);
2475
2476 LogFlowFunc(("returns %Vrc, pszComment=\"%s\"\n", rc, pszComment));
2477 return rc;
2478}
2479
2480/**
2481 * Changes the comment line of image in HDD container.
2482 *
2483 * @returns VBox status code.
2484 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2485 * @param pDisk Pointer to HDD container.
2486 * @param nImage Image number, counts from 0. 0 is always base image of container.
2487 * @param pszComment New comment string (UTF-8). NULL is allowed to reset the comment.
2488 */
2489VBOXDDU_DECL(int) VDSetComment(PVBOXHDD pDisk, unsigned nImage,
2490 const char *pszComment)
2491{
2492 int rc;
2493
2494 LogFlowFunc(("pDisk=%#p nImage=%u pszComment=%#p \"%s\"\n",
2495 pDisk, nImage, pszComment, pszComment));
2496 do
2497 {
2498 /* sanity check */
2499 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2500 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2501
2502 /* Check arguments. */
2503 AssertMsgBreak(VALID_PTR(pszComment) || pszComment == NULL,
2504 ("pszComment=%#p \"%s\"\n", pszComment, pszComment),
2505 rc = VERR_INVALID_PARAMETER);
2506
2507 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2508 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2509
2510 rc = pDisk->Backend->pfnSetComment(pImage->pvBackendData, pszComment);
2511 } while (0);
2512
2513 LogFlowFunc(("returns %Vrc\n", rc));
2514 return rc;
2515}
2516
2517
2518/**
2519 * Get UUID of image in HDD container.
2520 *
2521 * @returns VBox status code.
2522 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2523 * @param pDisk Pointer to HDD container.
2524 * @param nImage Image number, counts from 0. 0 is always base image of container.
2525 * @param pUuid Where to store the image creation UUID.
2526 */
2527VBOXDDU_DECL(int) VDGetUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
2528{
2529 int rc;
2530
2531 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
2532 do
2533 {
2534 /* sanity check */
2535 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2536 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2537
2538 /* Check arguments. */
2539 AssertMsgBreak(VALID_PTR(pUuid),
2540 ("pUuid=%#p\n", pUuid),
2541 rc = VERR_INVALID_PARAMETER);
2542
2543 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2544 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2545
2546 rc = pDisk->Backend->pfnGetUuid(pImage->pvBackendData, pUuid);
2547 } while (0);
2548
2549 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
2550 return rc;
2551}
2552
2553/**
2554 * Set the image's UUID. Should not be used by normal applications.
2555 *
2556 * @returns VBox status code.
2557 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2558 * @param pDisk Pointer to HDD container.
2559 * @param nImage Image number, counts from 0. 0 is always base image of container.
2560 * @param pUuid New UUID of the image. If NULL, a new UUID is created.
2561 */
2562VBOXDDU_DECL(int) VDSetUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
2563{
2564 int rc;
2565
2566 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
2567 pDisk, nImage, pUuid, pUuid));
2568 do
2569 {
2570 /* sanity check */
2571 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2572 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2573
2574 AssertMsgBreak(VALID_PTR(pUuid) || pUuid == NULL,
2575 ("pUuid=%#p\n", pUuid),
2576 rc = VERR_INVALID_PARAMETER);
2577
2578 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2579 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2580
2581 RTUUID Uuid;
2582 if (!pUuid)
2583 {
2584 RTUuidCreate(&Uuid);
2585 pUuid = &Uuid;
2586 }
2587 rc = pDisk->Backend->pfnSetUuid(pImage->pvBackendData, pUuid);
2588 } while (0);
2589
2590 LogFlowFunc(("returns %Vrc\n", rc));
2591 return rc;
2592}
2593
2594/**
2595 * Get last modification UUID of image in HDD container.
2596 *
2597 * @returns VBox status code.
2598 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2599 * @param pDisk Pointer to HDD container.
2600 * @param nImage Image number, counts from 0. 0 is always base image of container.
2601 * @param pUuid Where to store the image modification UUID.
2602 */
2603VBOXDDU_DECL(int) VDGetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PRTUUID pUuid)
2604{
2605 int rc = VINF_SUCCESS;
2606
2607 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
2608 do
2609 {
2610 /* sanity check */
2611 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2612 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2613
2614 /* Check arguments. */
2615 AssertMsgBreak(VALID_PTR(pUuid),
2616 ("pUuid=%#p\n", pUuid),
2617 rc = VERR_INVALID_PARAMETER);
2618
2619 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2620 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2621
2622 rc = pDisk->Backend->pfnGetModificationUuid(pImage->pvBackendData,
2623 pUuid);
2624 } while (0);
2625
2626 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
2627 return rc;
2628}
2629
2630/**
2631 * Set the image's last modification UUID. Should not be used by normal applications.
2632 *
2633 * @returns VBox status code.
2634 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2635 * @param pDisk Pointer to HDD container.
2636 * @param nImage Image number, counts from 0. 0 is always base image of container.
2637 * @param pUuid New modification UUID of the image. If NULL, a new UUID is created.
2638 */
2639VBOXDDU_DECL(int) VDSetModificationUuid(PVBOXHDD pDisk, unsigned nImage, PCRTUUID pUuid)
2640{
2641 int rc;
2642
2643 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
2644 pDisk, nImage, pUuid, pUuid));
2645 do
2646 {
2647 /* sanity check */
2648 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2649 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2650
2651 /* Check arguments. */
2652 AssertMsgBreak(VALID_PTR(pUuid) || pUuid == NULL,
2653 ("pUuid=%#p\n", pUuid),
2654 rc = VERR_INVALID_PARAMETER);
2655
2656 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2657 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2658
2659 RTUUID Uuid;
2660 if (!pUuid)
2661 {
2662 RTUuidCreate(&Uuid);
2663 pUuid = &Uuid;
2664 }
2665 rc = pDisk->Backend->pfnSetModificationUuid(pImage->pvBackendData,
2666 pUuid);
2667 } while (0);
2668
2669 LogFlowFunc(("returns %Vrc\n", rc));
2670 return rc;
2671}
2672
2673/**
2674 * Get parent UUID of image in HDD container.
2675 *
2676 * @returns VBox status code.
2677 * @returns VERR_VDI_IMAGE_NOT_FOUND if image with specified number was not opened.
2678 * @param pDisk Pointer to HDD container.
2679 * @param nImage Image number, counts from 0. 0 is always base image of container.
2680 * @param pUuid Where to store the parent image UUID.
2681 */
2682VBOXDDU_DECL(int) VDGetParentUuid(PVBOXHDD pDisk, unsigned nImage,
2683 PRTUUID pUuid)
2684{
2685 int rc = VINF_SUCCESS;
2686
2687 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p\n", pDisk, nImage, pUuid));
2688 do
2689 {
2690 /* sanity check */
2691 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2692 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2693
2694 /* Check arguments. */
2695 AssertMsgBreak(VALID_PTR(pUuid),
2696 ("pUuid=%#p\n", pUuid),
2697 rc = VERR_INVALID_PARAMETER);
2698
2699 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2700 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2701
2702 rc = pDisk->Backend->pfnGetParentUuid(pImage->pvBackendData, pUuid);
2703 } while (0);
2704
2705 LogFlowFunc(("returns %Vrc, Uuid={%Vuuid}\n", rc, pUuid));
2706 return rc;
2707}
2708
2709/**
2710 * Set the image's parent UUID. Should not be used by normal applications.
2711 *
2712 * @returns VBox status code.
2713 * @param pDisk Pointer to HDD container.
2714 * @param nImage Image number, counts from 0. 0 is always base image of container.
2715 * @param pUuid New parent UUID of the image. If NULL, a new UUID is created.
2716 */
2717VBOXDDU_DECL(int) VDSetParentUuid(PVBOXHDD pDisk, unsigned nImage,
2718 PCRTUUID pUuid)
2719{
2720 int rc;
2721
2722 LogFlowFunc(("pDisk=%#p nImage=%u pUuid=%#p {%Vuuid}\n",
2723 pDisk, nImage, pUuid, pUuid));
2724 do
2725 {
2726 /* sanity check */
2727 AssertBreak(VALID_PTR(pDisk), rc = VERR_INVALID_PARAMETER);
2728 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2729
2730 /* Check arguments. */
2731 AssertMsgBreak(VALID_PTR(pUuid) || pUuid == NULL,
2732 ("pUuid=%#p\n", pUuid),
2733 rc = VERR_INVALID_PARAMETER);
2734
2735 PVDIMAGE pImage = vdGetImageByNumber(pDisk, nImage);
2736 AssertBreak(pImage, rc = VERR_VDI_IMAGE_NOT_FOUND);
2737
2738 RTUUID Uuid;
2739 if (!pUuid)
2740 {
2741 RTUuidCreate(&Uuid);
2742 pUuid = &Uuid;
2743 }
2744 rc = pDisk->Backend->pfnSetParentUuid(pImage->pvBackendData, pUuid);
2745 } while (0);
2746
2747 LogFlowFunc(("returns %Vrc\n", rc));
2748 return rc;
2749}
2750
2751
2752/**
2753 * Debug helper - dumps all opened images in HDD container into the log file.
2754 *
2755 * @param pDisk Pointer to HDD container.
2756 */
2757VBOXDDU_DECL(void) VDDumpImages(PVBOXHDD pDisk)
2758{
2759 do
2760 {
2761 /* sanity check */
2762 AssertBreak(VALID_PTR(pDisk), );
2763 AssertMsg(pDisk->u32Signature == VBOXHDDDISK_SIGNATURE, ("u32Signature=%08x\n", pDisk->u32Signature));
2764
2765 RTLogPrintf("--- Dumping VD Disk, Backend=%s, Images=%u\n",
2766 pDisk->Backend->pszBackendName, pDisk->cImages);
2767 for (PVDIMAGE pImage = pDisk->pBase; pImage; pImage = pImage->pNext)
2768 {
2769 RTLogPrintf("Dumping VD image \"%s\"\n", pImage->pszFilename);
2770 pDisk->Backend->pfnDump(pImage->pvBackendData);
2771 }
2772 } while (0);
2773}
2774
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