VirtualBox

source: vbox/trunk/src/VBox/Storage/VMDK.cpp

Last change on this file was 99963, checked in by vboxsync, 11 months ago

VBox/types.h,pdmnetifs.h: Replaced PDMDATASEG with RTSGSEG since the two structures are identical. This should make it easy to create a RTSGBUF for a PDMSCATTERGATHER buffer (just limit the operations to cbUsed bytes).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 379.2 KB
Line 
1/* $Id: VMDK.cpp 99963 2023-05-24 23:27:54Z vboxsync $ */
2/** @file
3 * VMDK disk image, core code.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_VD_VMDK
33#include <VBox/log.h> /* before VBox/vd-ifs.h */
34#include <VBox/vd-plugin.h>
35#include <VBox/err.h>
36
37#include <iprt/assert.h>
38#include <iprt/alloc.h>
39#include <iprt/base64.h>
40#include <iprt/ctype.h>
41#include <iprt/crc.h>
42#include <iprt/dvm.h>
43#include <iprt/uuid.h>
44#include <iprt/path.h>
45#include <iprt/rand.h>
46#include <iprt/sg.h>
47#include <iprt/sort.h>
48#include <iprt/string.h>
49#include <iprt/zip.h>
50#include <iprt/asm.h>
51#ifdef RT_OS_WINDOWS
52# include <iprt/utf16.h>
53# include <iprt/uni.h>
54# include <iprt/uni.h>
55# include <iprt/nt/nt-and-windows.h>
56# include <winioctl.h>
57#endif
58#ifdef RT_OS_LINUX
59# include <errno.h>
60# include <sys/stat.h>
61# include <iprt/dir.h>
62# include <iprt/symlink.h>
63# include <iprt/linux/sysfs.h>
64#endif
65#ifdef RT_OS_FREEBSD
66#include <libgeom.h>
67#include <sys/stat.h>
68#include <stdlib.h>
69#endif
70#ifdef RT_OS_SOLARIS
71#include <sys/dkio.h>
72#include <sys/vtoc.h>
73#include <sys/efi_partition.h>
74#include <unistd.h>
75#include <errno.h>
76#endif
77#ifdef RT_OS_DARWIN
78# include <sys/stat.h>
79# include <sys/disk.h>
80# include <errno.h>
81/* The following structure and IOCTLs are defined in znu bsd/sys/disk.h but
82 inside KERNEL ifdefs and thus stripped from the SDK edition of the header.
83 While we could try include the header from the Kernel.framework, it's a lot
84 easier to just add the structure and 4 defines here. */
85typedef struct
86{
87 uint64_t offset;
88 uint64_t length;
89 uint8_t reserved0128[12];
90 dev_t dev;
91} dk_physical_extent_t;
92# define DKIOCGETBASE _IOR( 'd', 73, uint64_t)
93# define DKIOCLOCKPHYSICALEXTENTS _IO( 'd', 81)
94# define DKIOCGETPHYSICALEXTENT _IOWR('d', 82, dk_physical_extent_t)
95# define DKIOCUNLOCKPHYSICALEXTENTS _IO( 'd', 83)
96#endif /* RT_OS_DARWIN */
97
98#include "VDBackends.h"
99
100
101/*********************************************************************************************************************************
102* Constants And Macros, Structures and Typedefs *
103*********************************************************************************************************************************/
104
105/** Maximum encoded string size (including NUL) we allow for VMDK images.
106 * Deliberately not set high to avoid running out of descriptor space. */
107#define VMDK_ENCODED_COMMENT_MAX 1024
108
109/** VMDK descriptor DDB entry for PCHS cylinders. */
110#define VMDK_DDB_GEO_PCHS_CYLINDERS "ddb.geometry.cylinders"
111
112/** VMDK descriptor DDB entry for PCHS heads. */
113#define VMDK_DDB_GEO_PCHS_HEADS "ddb.geometry.heads"
114
115/** VMDK descriptor DDB entry for PCHS sectors. */
116#define VMDK_DDB_GEO_PCHS_SECTORS "ddb.geometry.sectors"
117
118/** VMDK descriptor DDB entry for LCHS cylinders. */
119#define VMDK_DDB_GEO_LCHS_CYLINDERS "ddb.geometry.biosCylinders"
120
121/** VMDK descriptor DDB entry for LCHS heads. */
122#define VMDK_DDB_GEO_LCHS_HEADS "ddb.geometry.biosHeads"
123
124/** VMDK descriptor DDB entry for LCHS sectors. */
125#define VMDK_DDB_GEO_LCHS_SECTORS "ddb.geometry.biosSectors"
126
127/** VMDK descriptor DDB entry for image UUID. */
128#define VMDK_DDB_IMAGE_UUID "ddb.uuid.image"
129
130/** VMDK descriptor DDB entry for image modification UUID. */
131#define VMDK_DDB_MODIFICATION_UUID "ddb.uuid.modification"
132
133/** VMDK descriptor DDB entry for parent image UUID. */
134#define VMDK_DDB_PARENT_UUID "ddb.uuid.parent"
135
136/** VMDK descriptor DDB entry for parent image modification UUID. */
137#define VMDK_DDB_PARENT_MODIFICATION_UUID "ddb.uuid.parentmodification"
138
139/** No compression for streamOptimized files. */
140#define VMDK_COMPRESSION_NONE 0
141
142/** Deflate compression for streamOptimized files. */
143#define VMDK_COMPRESSION_DEFLATE 1
144
145/** Marker that the actual GD value is stored in the footer. */
146#define VMDK_GD_AT_END 0xffffffffffffffffULL
147
148/** Marker for end-of-stream in streamOptimized images. */
149#define VMDK_MARKER_EOS 0
150
151/** Marker for grain table block in streamOptimized images. */
152#define VMDK_MARKER_GT 1
153
154/** Marker for grain directory block in streamOptimized images. */
155#define VMDK_MARKER_GD 2
156
157/** Marker for footer in streamOptimized images. */
158#define VMDK_MARKER_FOOTER 3
159
160/** Marker for unknown purpose in streamOptimized images.
161 * Shows up in very recent images created by vSphere, but only sporadically.
162 * They "forgot" to document that one in the VMDK specification. */
163#define VMDK_MARKER_UNSPECIFIED 4
164
165/** Dummy marker for "don't check the marker value". */
166#define VMDK_MARKER_IGNORE 0xffffffffU
167
168/**
169 * Magic number for hosted images created by VMware Workstation 4, VMware
170 * Workstation 5, VMware Server or VMware Player. Not necessarily sparse.
171 */
172#define VMDK_SPARSE_MAGICNUMBER 0x564d444b /* 'V' 'M' 'D' 'K' */
173
174/** VMDK sector size in bytes. */
175#define VMDK_SECTOR_SIZE 512
176/** Max string buffer size for uint64_t with null term */
177#define UINT64_MAX_BUFF_SIZE 21
178/** Grain directory entry size in bytes */
179#define VMDK_GRAIN_DIR_ENTRY_SIZE 4
180/** Grain table size in bytes */
181#define VMDK_GRAIN_TABLE_SIZE 2048
182
183/**
184 * VMDK hosted binary extent header. The "Sparse" is a total misnomer, as
185 * this header is also used for monolithic flat images.
186 */
187#pragma pack(1)
188typedef struct SparseExtentHeader
189{
190 uint32_t magicNumber;
191 uint32_t version;
192 uint32_t flags;
193 uint64_t capacity;
194 uint64_t grainSize;
195 uint64_t descriptorOffset;
196 uint64_t descriptorSize;
197 uint32_t numGTEsPerGT;
198 uint64_t rgdOffset;
199 uint64_t gdOffset;
200 uint64_t overHead;
201 bool uncleanShutdown;
202 char singleEndLineChar;
203 char nonEndLineChar;
204 char doubleEndLineChar1;
205 char doubleEndLineChar2;
206 uint16_t compressAlgorithm;
207 uint8_t pad[433];
208} SparseExtentHeader;
209#pragma pack()
210
211/** The maximum allowed descriptor size in the extent header in sectors. */
212#define VMDK_SPARSE_DESCRIPTOR_SIZE_MAX UINT64_C(20480) /* 10MB */
213
214/** VMDK capacity for a single chunk when 2G splitting is turned on. Should be
215 * divisible by the default grain size (64K) */
216#define VMDK_2G_SPLIT_SIZE (2047 * 1024 * 1024)
217
218/** VMDK streamOptimized file format marker. The type field may or may not
219 * be actually valid, but there's always data to read there. */
220#pragma pack(1)
221typedef struct VMDKMARKER
222{
223 uint64_t uSector;
224 uint32_t cbSize;
225 uint32_t uType;
226} VMDKMARKER, *PVMDKMARKER;
227#pragma pack()
228
229
230/** Convert sector number/size to byte offset/size. */
231#define VMDK_SECTOR2BYTE(u) ((uint64_t)(u) << 9)
232
233/** Convert byte offset/size to sector number/size. */
234#define VMDK_BYTE2SECTOR(u) ((u) >> 9)
235
236/**
237 * VMDK extent type.
238 */
239typedef enum VMDKETYPE
240{
241 /** Hosted sparse extent. */
242 VMDKETYPE_HOSTED_SPARSE = 1,
243 /** Flat extent. */
244 VMDKETYPE_FLAT,
245 /** Zero extent. */
246 VMDKETYPE_ZERO,
247 /** VMFS extent, used by ESX. */
248 VMDKETYPE_VMFS
249} VMDKETYPE, *PVMDKETYPE;
250
251/**
252 * VMDK access type for a extent.
253 */
254typedef enum VMDKACCESS
255{
256 /** No access allowed. */
257 VMDKACCESS_NOACCESS = 0,
258 /** Read-only access. */
259 VMDKACCESS_READONLY,
260 /** Read-write access. */
261 VMDKACCESS_READWRITE
262} VMDKACCESS, *PVMDKACCESS;
263
264/** Forward declaration for PVMDKIMAGE. */
265typedef struct VMDKIMAGE *PVMDKIMAGE;
266
267/**
268 * Extents files entry. Used for opening a particular file only once.
269 */
270typedef struct VMDKFILE
271{
272 /** Pointer to file path. Local copy. */
273 const char *pszFilename;
274 /** Pointer to base name. Local copy. */
275 const char *pszBasename;
276 /** File open flags for consistency checking. */
277 unsigned fOpen;
278 /** Handle for sync/async file abstraction.*/
279 PVDIOSTORAGE pStorage;
280 /** Reference counter. */
281 unsigned uReferences;
282 /** Flag whether the file should be deleted on last close. */
283 bool fDelete;
284 /** Pointer to the image we belong to (for debugging purposes). */
285 PVMDKIMAGE pImage;
286 /** Pointer to next file descriptor. */
287 struct VMDKFILE *pNext;
288 /** Pointer to the previous file descriptor. */
289 struct VMDKFILE *pPrev;
290} VMDKFILE, *PVMDKFILE;
291
292/**
293 * VMDK extent data structure.
294 */
295typedef struct VMDKEXTENT
296{
297 /** File handle. */
298 PVMDKFILE pFile;
299 /** Base name of the image extent. */
300 const char *pszBasename;
301 /** Full name of the image extent. */
302 const char *pszFullname;
303 /** Number of sectors in this extent. */
304 uint64_t cSectors;
305 /** Number of sectors per block (grain in VMDK speak). */
306 uint64_t cSectorsPerGrain;
307 /** Starting sector number of descriptor. */
308 uint64_t uDescriptorSector;
309 /** Size of descriptor in sectors. */
310 uint64_t cDescriptorSectors;
311 /** Starting sector number of grain directory. */
312 uint64_t uSectorGD;
313 /** Starting sector number of redundant grain directory. */
314 uint64_t uSectorRGD;
315 /** Total number of metadata sectors. */
316 uint64_t cOverheadSectors;
317 /** Nominal size (i.e. as described by the descriptor) of this extent. */
318 uint64_t cNominalSectors;
319 /** Sector offset (i.e. as described by the descriptor) of this extent. */
320 uint64_t uSectorOffset;
321 /** Number of entries in a grain table. */
322 uint32_t cGTEntries;
323 /** Number of sectors reachable via a grain directory entry. */
324 uint32_t cSectorsPerGDE;
325 /** Number of entries in the grain directory. */
326 uint32_t cGDEntries;
327 /** Pointer to the next free sector. Legacy information. Do not use. */
328 uint32_t uFreeSector;
329 /** Number of this extent in the list of images. */
330 uint32_t uExtent;
331 /** Pointer to the descriptor (NULL if no descriptor in this extent). */
332 char *pDescData;
333 /** Pointer to the grain directory. */
334 uint32_t *pGD;
335 /** Pointer to the redundant grain directory. */
336 uint32_t *pRGD;
337 /** VMDK version of this extent. 1=1.0/1.1 */
338 uint32_t uVersion;
339 /** Type of this extent. */
340 VMDKETYPE enmType;
341 /** Access to this extent. */
342 VMDKACCESS enmAccess;
343 /** Flag whether this extent is marked as unclean. */
344 bool fUncleanShutdown;
345 /** Flag whether the metadata in the extent header needs to be updated. */
346 bool fMetaDirty;
347 /** Flag whether there is a footer in this extent. */
348 bool fFooter;
349 /** Compression type for this extent. */
350 uint16_t uCompression;
351 /** Append position for writing new grain. Only for sparse extents. */
352 uint64_t uAppendPosition;
353 /** Last grain which was accessed. Only for streamOptimized extents. */
354 uint32_t uLastGrainAccess;
355 /** Starting sector corresponding to the grain buffer. */
356 uint32_t uGrainSectorAbs;
357 /** Grain number corresponding to the grain buffer. */
358 uint32_t uGrain;
359 /** Actual size of the compressed data, only valid for reading. */
360 uint32_t cbGrainStreamRead;
361 /** Size of compressed grain buffer for streamOptimized extents. */
362 size_t cbCompGrain;
363 /** Compressed grain buffer for streamOptimized extents, with marker. */
364 void *pvCompGrain;
365 /** Decompressed grain buffer for streamOptimized extents. */
366 void *pvGrain;
367 /** Reference to the image in which this extent is used. Do not use this
368 * on a regular basis to avoid passing pImage references to functions
369 * explicitly. */
370 struct VMDKIMAGE *pImage;
371} VMDKEXTENT, *PVMDKEXTENT;
372
373/**
374 * Grain table cache size. Allocated per image.
375 */
376#define VMDK_GT_CACHE_SIZE 256
377
378/**
379 * Grain table block size. Smaller than an actual grain table block to allow
380 * more grain table blocks to be cached without having to allocate excessive
381 * amounts of memory for the cache.
382 */
383#define VMDK_GT_CACHELINE_SIZE 128
384
385
386/**
387 * Maximum number of lines in a descriptor file. Not worth the effort of
388 * making it variable. Descriptor files are generally very short (~20 lines),
389 * with the exception of sparse files split in 2G chunks, which need for the
390 * maximum size (almost 2T) exactly 1025 lines for the disk database.
391 */
392#define VMDK_DESCRIPTOR_LINES_MAX 1100U
393
394/**
395 * Parsed descriptor information. Allows easy access and update of the
396 * descriptor (whether separate file or not). Free form text files suck.
397 */
398typedef struct VMDKDESCRIPTOR
399{
400 /** Line number of first entry of the disk descriptor. */
401 unsigned uFirstDesc;
402 /** Line number of first entry in the extent description. */
403 unsigned uFirstExtent;
404 /** Line number of first disk database entry. */
405 unsigned uFirstDDB;
406 /** Total number of lines. */
407 unsigned cLines;
408 /** Total amount of memory available for the descriptor. */
409 size_t cbDescAlloc;
410 /** Set if descriptor has been changed and not yet written to disk. */
411 bool fDirty;
412 /** Array of pointers to the data in the descriptor. */
413 char *aLines[VMDK_DESCRIPTOR_LINES_MAX];
414 /** Array of line indices pointing to the next non-comment line. */
415 unsigned aNextLines[VMDK_DESCRIPTOR_LINES_MAX];
416} VMDKDESCRIPTOR, *PVMDKDESCRIPTOR;
417
418
419/**
420 * Cache entry for translating extent/sector to a sector number in that
421 * extent.
422 */
423typedef struct VMDKGTCACHEENTRY
424{
425 /** Extent number for which this entry is valid. */
426 uint32_t uExtent;
427 /** GT data block number. */
428 uint64_t uGTBlock;
429 /** Data part of the cache entry. */
430 uint32_t aGTData[VMDK_GT_CACHELINE_SIZE];
431} VMDKGTCACHEENTRY, *PVMDKGTCACHEENTRY;
432
433/**
434 * Cache data structure for blocks of grain table entries. For now this is a
435 * fixed size direct mapping cache, but this should be adapted to the size of
436 * the sparse image and maybe converted to a set-associative cache. The
437 * implementation below implements a write-through cache with write allocate.
438 */
439typedef struct VMDKGTCACHE
440{
441 /** Cache entries. */
442 VMDKGTCACHEENTRY aGTCache[VMDK_GT_CACHE_SIZE];
443 /** Number of cache entries (currently unused). */
444 unsigned cEntries;
445} VMDKGTCACHE, *PVMDKGTCACHE;
446
447/**
448 * Complete VMDK image data structure. Mainly a collection of extents and a few
449 * extra global data fields.
450 */
451typedef struct VMDKIMAGE
452{
453 /** Image name. */
454 const char *pszFilename;
455 /** Descriptor file if applicable. */
456 PVMDKFILE pFile;
457
458 /** Pointer to the per-disk VD interface list. */
459 PVDINTERFACE pVDIfsDisk;
460 /** Pointer to the per-image VD interface list. */
461 PVDINTERFACE pVDIfsImage;
462
463 /** Error interface. */
464 PVDINTERFACEERROR pIfError;
465 /** I/O interface. */
466 PVDINTERFACEIOINT pIfIo;
467
468
469 /** Pointer to the image extents. */
470 PVMDKEXTENT pExtents;
471 /** Number of image extents. */
472 unsigned cExtents;
473 /** Pointer to the files list, for opening a file referenced multiple
474 * times only once (happens mainly with raw partition access). */
475 PVMDKFILE pFiles;
476
477 /**
478 * Pointer to an array of segment entries for async I/O.
479 * This is an optimization because the task number to submit is not known
480 * and allocating/freeing an array in the read/write functions every time
481 * is too expensive.
482 */
483 PRTSGSEG paSegments;
484 /** Entries available in the segments array. */
485 unsigned cSegments;
486
487 /** Open flags passed by VBoxHD layer. */
488 unsigned uOpenFlags;
489 /** Image flags defined during creation or determined during open. */
490 unsigned uImageFlags;
491 /** Total size of the image. */
492 uint64_t cbSize;
493 /** Physical geometry of this image. */
494 VDGEOMETRY PCHSGeometry;
495 /** Logical geometry of this image. */
496 VDGEOMETRY LCHSGeometry;
497 /** Image UUID. */
498 RTUUID ImageUuid;
499 /** Image modification UUID. */
500 RTUUID ModificationUuid;
501 /** Parent image UUID. */
502 RTUUID ParentUuid;
503 /** Parent image modification UUID. */
504 RTUUID ParentModificationUuid;
505
506 /** Pointer to grain table cache, if this image contains sparse extents. */
507 PVMDKGTCACHE pGTCache;
508 /** Pointer to the descriptor (NULL if no separate descriptor file). */
509 char *pDescData;
510 /** Allocation size of the descriptor file. */
511 size_t cbDescAlloc;
512 /** Parsed descriptor file content. */
513 VMDKDESCRIPTOR Descriptor;
514 /** The static region list. */
515 VDREGIONLIST RegionList;
516} VMDKIMAGE;
517
518
519/** State for the input/output callout of the inflate reader/deflate writer. */
520typedef struct VMDKCOMPRESSIO
521{
522 /* Image this operation relates to. */
523 PVMDKIMAGE pImage;
524 /* Current read position. */
525 ssize_t iOffset;
526 /* Size of the compressed grain buffer (available data). */
527 size_t cbCompGrain;
528 /* Pointer to the compressed grain buffer. */
529 void *pvCompGrain;
530} VMDKCOMPRESSIO;
531
532
533/** Tracks async grain allocation. */
534typedef struct VMDKGRAINALLOCASYNC
535{
536 /** Flag whether the allocation failed. */
537 bool fIoErr;
538 /** Current number of transfers pending.
539 * If reached 0 and there is an error the old state is restored. */
540 unsigned cIoXfersPending;
541 /** Sector number */
542 uint64_t uSector;
543 /** Flag whether the grain table needs to be updated. */
544 bool fGTUpdateNeeded;
545 /** Extent the allocation happens. */
546 PVMDKEXTENT pExtent;
547 /** Position of the new grain, required for the grain table update. */
548 uint64_t uGrainOffset;
549 /** Grain table sector. */
550 uint64_t uGTSector;
551 /** Backup grain table sector. */
552 uint64_t uRGTSector;
553} VMDKGRAINALLOCASYNC, *PVMDKGRAINALLOCASYNC;
554
555/**
556 * State information for vmdkRename() and helpers.
557 */
558typedef struct VMDKRENAMESTATE
559{
560 /** Array of old filenames. */
561 char **apszOldName;
562 /** Array of new filenames. */
563 char **apszNewName;
564 /** Array of new lines in the extent descriptor. */
565 char **apszNewLines;
566 /** Name of the old descriptor file if not a sparse image. */
567 char *pszOldDescName;
568 /** Flag whether we called vmdkFreeImage(). */
569 bool fImageFreed;
570 /** Flag whther the descriptor is embedded in the image (sparse) or
571 * in a separate file. */
572 bool fEmbeddedDesc;
573 /** Number of extents in the image. */
574 unsigned cExtents;
575 /** New base filename. */
576 char *pszNewBaseName;
577 /** The old base filename. */
578 char *pszOldBaseName;
579 /** New full filename. */
580 char *pszNewFullName;
581 /** Old full filename. */
582 char *pszOldFullName;
583 /** The old image name. */
584 const char *pszOldImageName;
585 /** Copy of the original VMDK descriptor. */
586 VMDKDESCRIPTOR DescriptorCopy;
587 /** Copy of the extent state for sparse images. */
588 VMDKEXTENT ExtentCopy;
589} VMDKRENAMESTATE;
590/** Pointer to a VMDK rename state. */
591typedef VMDKRENAMESTATE *PVMDKRENAMESTATE;
592
593
594/*********************************************************************************************************************************
595* Static Variables *
596*********************************************************************************************************************************/
597
598/** NULL-terminated array of supported file extensions. */
599static const VDFILEEXTENSION s_aVmdkFileExtensions[] =
600{
601 {"vmdk", VDTYPE_HDD},
602 {NULL, VDTYPE_INVALID}
603};
604
605/** NULL-terminated array of configuration option. */
606static const VDCONFIGINFO s_aVmdkConfigInfo[] =
607{
608 /* Options for VMDK raw disks */
609 { "RawDrive", NULL, VDCFGVALUETYPE_STRING, 0 },
610 { "Partitions", NULL, VDCFGVALUETYPE_STRING, 0 },
611 { "BootSector", NULL, VDCFGVALUETYPE_BYTES, 0 },
612 { "Relative", NULL, VDCFGVALUETYPE_INTEGER, 0 },
613
614 /* End of options list */
615 { NULL, NULL, VDCFGVALUETYPE_INTEGER, 0 }
616};
617
618
619/*********************************************************************************************************************************
620* Internal Functions *
621*********************************************************************************************************************************/
622
623static void vmdkFreeStreamBuffers(PVMDKEXTENT pExtent);
624static int vmdkFreeExtentData(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
625 bool fDelete);
626
627static int vmdkCreateExtents(PVMDKIMAGE pImage, unsigned cExtents);
628static int vmdkFlushImage(PVMDKIMAGE pImage, PVDIOCTX pIoCtx);
629static int vmdkSetImageComment(PVMDKIMAGE pImage, const char *pszComment);
630static int vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete, bool fFlush);
631
632static DECLCALLBACK(int) vmdkAllocGrainComplete(void *pBackendData, PVDIOCTX pIoCtx,
633 void *pvUser, int rcReq);
634
635/**
636 * Internal: open a file (using a file descriptor cache to ensure each file
637 * is only opened once - anything else can cause locking problems).
638 */
639static int vmdkFileOpen(PVMDKIMAGE pImage, PVMDKFILE *ppVmdkFile,
640 const char *pszBasename, const char *pszFilename, uint32_t fOpen)
641{
642 int rc = VINF_SUCCESS;
643 PVMDKFILE pVmdkFile;
644
645 for (pVmdkFile = pImage->pFiles;
646 pVmdkFile != NULL;
647 pVmdkFile = pVmdkFile->pNext)
648 {
649 if (!strcmp(pszFilename, pVmdkFile->pszFilename))
650 {
651 Assert(fOpen == pVmdkFile->fOpen);
652 pVmdkFile->uReferences++;
653
654 *ppVmdkFile = pVmdkFile;
655
656 return rc;
657 }
658 }
659
660 /* If we get here, there's no matching entry in the cache. */
661 pVmdkFile = (PVMDKFILE)RTMemAllocZ(sizeof(VMDKFILE));
662 if (!pVmdkFile)
663 {
664 *ppVmdkFile = NULL;
665 return VERR_NO_MEMORY;
666 }
667
668 pVmdkFile->pszFilename = RTStrDup(pszFilename);
669 if (!pVmdkFile->pszFilename)
670 {
671 RTMemFree(pVmdkFile);
672 *ppVmdkFile = NULL;
673 return VERR_NO_MEMORY;
674 }
675
676 if (pszBasename)
677 {
678 pVmdkFile->pszBasename = RTStrDup(pszBasename);
679 if (!pVmdkFile->pszBasename)
680 {
681 RTStrFree((char *)(void *)pVmdkFile->pszFilename);
682 RTMemFree(pVmdkFile);
683 *ppVmdkFile = NULL;
684 return VERR_NO_MEMORY;
685 }
686 }
687
688 pVmdkFile->fOpen = fOpen;
689
690 rc = vdIfIoIntFileOpen(pImage->pIfIo, pszFilename, fOpen,
691 &pVmdkFile->pStorage);
692 if (RT_SUCCESS(rc))
693 {
694 pVmdkFile->uReferences = 1;
695 pVmdkFile->pImage = pImage;
696 pVmdkFile->pNext = pImage->pFiles;
697 if (pImage->pFiles)
698 pImage->pFiles->pPrev = pVmdkFile;
699 pImage->pFiles = pVmdkFile;
700 *ppVmdkFile = pVmdkFile;
701 }
702 else
703 {
704 RTStrFree((char *)(void *)pVmdkFile->pszFilename);
705 RTMemFree(pVmdkFile);
706 *ppVmdkFile = NULL;
707 }
708
709 return rc;
710}
711
712/**
713 * Internal: close a file, updating the file descriptor cache.
714 */
715static int vmdkFileClose(PVMDKIMAGE pImage, PVMDKFILE *ppVmdkFile, bool fDelete)
716{
717 int rc = VINF_SUCCESS;
718 PVMDKFILE pVmdkFile = *ppVmdkFile;
719
720 AssertPtr(pVmdkFile);
721
722 pVmdkFile->fDelete |= fDelete;
723 Assert(pVmdkFile->uReferences);
724 pVmdkFile->uReferences--;
725 if (pVmdkFile->uReferences == 0)
726 {
727 PVMDKFILE pPrev;
728 PVMDKFILE pNext;
729
730 /* Unchain the element from the list. */
731 pPrev = pVmdkFile->pPrev;
732 pNext = pVmdkFile->pNext;
733
734 if (pNext)
735 pNext->pPrev = pPrev;
736 if (pPrev)
737 pPrev->pNext = pNext;
738 else
739 pImage->pFiles = pNext;
740
741 rc = vdIfIoIntFileClose(pImage->pIfIo, pVmdkFile->pStorage);
742
743 bool fFileDel = pVmdkFile->fDelete;
744 if ( pVmdkFile->pszBasename
745 && fFileDel)
746 {
747 const char *pszSuffix = RTPathSuffix(pVmdkFile->pszBasename);
748 if ( RTPathHasPath(pVmdkFile->pszBasename)
749 || !pszSuffix
750 || ( strcmp(pszSuffix, ".vmdk")
751 && strcmp(pszSuffix, ".bin")
752 && strcmp(pszSuffix, ".img")))
753 fFileDel = false;
754 }
755
756 if (fFileDel)
757 {
758 int rc2 = vdIfIoIntFileDelete(pImage->pIfIo, pVmdkFile->pszFilename);
759 if (RT_SUCCESS(rc))
760 rc = rc2;
761 }
762 else if (pVmdkFile->fDelete)
763 LogRel(("VMDK: Denying deletion of %s\n", pVmdkFile->pszBasename));
764 RTStrFree((char *)(void *)pVmdkFile->pszFilename);
765 if (pVmdkFile->pszBasename)
766 RTStrFree((char *)(void *)pVmdkFile->pszBasename);
767 RTMemFree(pVmdkFile);
768 }
769
770 *ppVmdkFile = NULL;
771 return rc;
772}
773
774/*#define VMDK_USE_BLOCK_DECOMP_API - test and enable */
775#ifndef VMDK_USE_BLOCK_DECOMP_API
776static DECLCALLBACK(int) vmdkFileInflateHelper(void *pvUser, void *pvBuf, size_t cbBuf, size_t *pcbBuf)
777{
778 VMDKCOMPRESSIO *pInflateState = (VMDKCOMPRESSIO *)pvUser;
779 size_t cbInjected = 0;
780
781 Assert(cbBuf);
782 if (pInflateState->iOffset < 0)
783 {
784 *(uint8_t *)pvBuf = RTZIPTYPE_ZLIB;
785 pvBuf = (uint8_t *)pvBuf + 1;
786 cbBuf--;
787 cbInjected = 1;
788 pInflateState->iOffset = RT_UOFFSETOF(VMDKMARKER, uType);
789 }
790 if (!cbBuf)
791 {
792 if (pcbBuf)
793 *pcbBuf = cbInjected;
794 return VINF_SUCCESS;
795 }
796 cbBuf = RT_MIN(cbBuf, pInflateState->cbCompGrain - pInflateState->iOffset);
797 memcpy(pvBuf,
798 (uint8_t *)pInflateState->pvCompGrain + pInflateState->iOffset,
799 cbBuf);
800 pInflateState->iOffset += cbBuf;
801 Assert(pcbBuf);
802 *pcbBuf = cbBuf + cbInjected;
803 return VINF_SUCCESS;
804}
805#endif
806
807/**
808 * Internal: read from a file and inflate the compressed data,
809 * distinguishing between async and normal operation
810 */
811DECLINLINE(int) vmdkFileInflateSync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
812 uint64_t uOffset, void *pvBuf,
813 size_t cbToRead, const void *pcvMarker,
814 uint64_t *puLBA, uint32_t *pcbMarkerData)
815{
816 int rc;
817#ifndef VMDK_USE_BLOCK_DECOMP_API
818 PRTZIPDECOMP pZip = NULL;
819#endif
820 VMDKMARKER *pMarker = (VMDKMARKER *)pExtent->pvCompGrain;
821 size_t cbCompSize, cbActuallyRead;
822
823 if (!pcvMarker)
824 {
825 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
826 uOffset, pMarker, RT_UOFFSETOF(VMDKMARKER, uType));
827 if (RT_FAILURE(rc))
828 return rc;
829 }
830 else
831 {
832 memcpy(pMarker, pcvMarker, RT_UOFFSETOF(VMDKMARKER, uType));
833 /* pcvMarker endianness has already been partially transformed, fix it */
834 pMarker->uSector = RT_H2LE_U64(pMarker->uSector);
835 pMarker->cbSize = RT_H2LE_U32(pMarker->cbSize);
836 }
837
838 cbCompSize = RT_LE2H_U32(pMarker->cbSize);
839 if (cbCompSize == 0)
840 {
841 AssertMsgFailed(("VMDK: corrupted marker\n"));
842 return VERR_VD_VMDK_INVALID_FORMAT;
843 }
844
845 /* Sanity check - the expansion ratio should be much less than 2. */
846 Assert(cbCompSize < 2 * cbToRead);
847 if (cbCompSize >= 2 * cbToRead)
848 return VERR_VD_VMDK_INVALID_FORMAT;
849
850 /* Compressed grain marker. Data follows immediately. */
851 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
852 uOffset + RT_UOFFSETOF(VMDKMARKER, uType),
853 (uint8_t *)pExtent->pvCompGrain
854 + RT_UOFFSETOF(VMDKMARKER, uType),
855 RT_ALIGN_Z( cbCompSize
856 + RT_UOFFSETOF(VMDKMARKER, uType),
857 512)
858 - RT_UOFFSETOF(VMDKMARKER, uType));
859
860 if (puLBA)
861 *puLBA = RT_LE2H_U64(pMarker->uSector);
862 if (pcbMarkerData)
863 *pcbMarkerData = RT_ALIGN( cbCompSize
864 + RT_UOFFSETOF(VMDKMARKER, uType),
865 512);
866
867#ifdef VMDK_USE_BLOCK_DECOMP_API
868 rc = RTZipBlockDecompress(RTZIPTYPE_ZLIB, 0 /*fFlags*/,
869 pExtent->pvCompGrain, cbCompSize + RT_UOFFSETOF(VMDKMARKER, uType), NULL,
870 pvBuf, cbToRead, &cbActuallyRead);
871#else
872 VMDKCOMPRESSIO InflateState;
873 InflateState.pImage = pImage;
874 InflateState.iOffset = -1;
875 InflateState.cbCompGrain = cbCompSize + RT_UOFFSETOF(VMDKMARKER, uType);
876 InflateState.pvCompGrain = pExtent->pvCompGrain;
877
878 rc = RTZipDecompCreate(&pZip, &InflateState, vmdkFileInflateHelper);
879 if (RT_FAILURE(rc))
880 return rc;
881 rc = RTZipDecompress(pZip, pvBuf, cbToRead, &cbActuallyRead);
882 RTZipDecompDestroy(pZip);
883#endif /* !VMDK_USE_BLOCK_DECOMP_API */
884 if (RT_FAILURE(rc))
885 {
886 if (rc == VERR_ZIP_CORRUPTED)
887 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: Compressed image is corrupted '%s'"), pExtent->pszFullname);
888 return rc;
889 }
890 if (cbActuallyRead != cbToRead)
891 rc = VERR_VD_VMDK_INVALID_FORMAT;
892 return rc;
893}
894
895static DECLCALLBACK(int) vmdkFileDeflateHelper(void *pvUser, const void *pvBuf, size_t cbBuf)
896{
897 VMDKCOMPRESSIO *pDeflateState = (VMDKCOMPRESSIO *)pvUser;
898
899 Assert(cbBuf);
900 if (pDeflateState->iOffset < 0)
901 {
902 pvBuf = (const uint8_t *)pvBuf + 1;
903 cbBuf--;
904 pDeflateState->iOffset = RT_UOFFSETOF(VMDKMARKER, uType);
905 }
906 if (!cbBuf)
907 return VINF_SUCCESS;
908 if (pDeflateState->iOffset + cbBuf > pDeflateState->cbCompGrain)
909 return VERR_BUFFER_OVERFLOW;
910 memcpy((uint8_t *)pDeflateState->pvCompGrain + pDeflateState->iOffset,
911 pvBuf, cbBuf);
912 pDeflateState->iOffset += cbBuf;
913 return VINF_SUCCESS;
914}
915
916/**
917 * Internal: deflate the uncompressed data and write to a file,
918 * distinguishing between async and normal operation
919 */
920DECLINLINE(int) vmdkFileDeflateSync(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
921 uint64_t uOffset, const void *pvBuf,
922 size_t cbToWrite, uint64_t uLBA,
923 uint32_t *pcbMarkerData)
924{
925 int rc;
926 PRTZIPCOMP pZip = NULL;
927 VMDKCOMPRESSIO DeflateState;
928
929 DeflateState.pImage = pImage;
930 DeflateState.iOffset = -1;
931 DeflateState.cbCompGrain = pExtent->cbCompGrain;
932 DeflateState.pvCompGrain = pExtent->pvCompGrain;
933
934 rc = RTZipCompCreate(&pZip, &DeflateState, vmdkFileDeflateHelper,
935 RTZIPTYPE_ZLIB, RTZIPLEVEL_DEFAULT);
936 if (RT_FAILURE(rc))
937 return rc;
938 rc = RTZipCompress(pZip, pvBuf, cbToWrite);
939 if (RT_SUCCESS(rc))
940 rc = RTZipCompFinish(pZip);
941 RTZipCompDestroy(pZip);
942 if (RT_SUCCESS(rc))
943 {
944 Assert( DeflateState.iOffset > 0
945 && (size_t)DeflateState.iOffset <= DeflateState.cbCompGrain);
946
947 /* pad with zeroes to get to a full sector size */
948 uint32_t uSize = DeflateState.iOffset;
949 if (uSize % 512)
950 {
951 uint32_t uSizeAlign = RT_ALIGN(uSize, 512);
952 memset((uint8_t *)pExtent->pvCompGrain + uSize, '\0',
953 uSizeAlign - uSize);
954 uSize = uSizeAlign;
955 }
956
957 if (pcbMarkerData)
958 *pcbMarkerData = uSize;
959
960 /* Compressed grain marker. Data follows immediately. */
961 VMDKMARKER *pMarker = (VMDKMARKER *)pExtent->pvCompGrain;
962 pMarker->uSector = RT_H2LE_U64(uLBA);
963 pMarker->cbSize = RT_H2LE_U32( DeflateState.iOffset
964 - RT_UOFFSETOF(VMDKMARKER, uType));
965 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
966 uOffset, pMarker, uSize);
967 if (RT_FAILURE(rc))
968 return rc;
969 }
970 return rc;
971}
972
973
974/**
975 * Internal: check if all files are closed, prevent leaking resources.
976 */
977static int vmdkFileCheckAllClose(PVMDKIMAGE pImage)
978{
979 int rc = VINF_SUCCESS, rc2;
980 PVMDKFILE pVmdkFile;
981
982 Assert(pImage->pFiles == NULL);
983 for (pVmdkFile = pImage->pFiles;
984 pVmdkFile != NULL;
985 pVmdkFile = pVmdkFile->pNext)
986 {
987 LogRel(("VMDK: leaking reference to file \"%s\"\n",
988 pVmdkFile->pszFilename));
989 pImage->pFiles = pVmdkFile->pNext;
990
991 rc2 = vmdkFileClose(pImage, &pVmdkFile, pVmdkFile->fDelete);
992
993 if (RT_SUCCESS(rc))
994 rc = rc2;
995 }
996 return rc;
997}
998
999/**
1000 * Internal: truncate a string (at a UTF8 code point boundary) and encode the
1001 * critical non-ASCII characters.
1002 */
1003static char *vmdkEncodeString(const char *psz)
1004{
1005 char szEnc[VMDK_ENCODED_COMMENT_MAX + 3];
1006 char *pszDst = szEnc;
1007
1008 AssertPtr(psz);
1009
1010 for (; *psz; psz = RTStrNextCp(psz))
1011 {
1012 char *pszDstPrev = pszDst;
1013 RTUNICP Cp = RTStrGetCp(psz);
1014 if (Cp == '\\')
1015 {
1016 pszDst = RTStrPutCp(pszDst, Cp);
1017 pszDst = RTStrPutCp(pszDst, Cp);
1018 }
1019 else if (Cp == '\n')
1020 {
1021 pszDst = RTStrPutCp(pszDst, '\\');
1022 pszDst = RTStrPutCp(pszDst, 'n');
1023 }
1024 else if (Cp == '\r')
1025 {
1026 pszDst = RTStrPutCp(pszDst, '\\');
1027 pszDst = RTStrPutCp(pszDst, 'r');
1028 }
1029 else
1030 pszDst = RTStrPutCp(pszDst, Cp);
1031 if (pszDst - szEnc >= VMDK_ENCODED_COMMENT_MAX - 1)
1032 {
1033 pszDst = pszDstPrev;
1034 break;
1035 }
1036 }
1037 *pszDst = '\0';
1038 return RTStrDup(szEnc);
1039}
1040
1041/**
1042 * Internal: decode a string and store it into the specified string.
1043 */
1044static int vmdkDecodeString(const char *pszEncoded, char *psz, size_t cb)
1045{
1046 int rc = VINF_SUCCESS;
1047 char szBuf[4];
1048
1049 if (!cb)
1050 return VERR_BUFFER_OVERFLOW;
1051
1052 AssertPtr(psz);
1053
1054 for (; *pszEncoded; pszEncoded = RTStrNextCp(pszEncoded))
1055 {
1056 char *pszDst = szBuf;
1057 RTUNICP Cp = RTStrGetCp(pszEncoded);
1058 if (Cp == '\\')
1059 {
1060 pszEncoded = RTStrNextCp(pszEncoded);
1061 RTUNICP CpQ = RTStrGetCp(pszEncoded);
1062 if (CpQ == 'n')
1063 RTStrPutCp(pszDst, '\n');
1064 else if (CpQ == 'r')
1065 RTStrPutCp(pszDst, '\r');
1066 else if (CpQ == '\0')
1067 {
1068 rc = VERR_VD_VMDK_INVALID_HEADER;
1069 break;
1070 }
1071 else
1072 RTStrPutCp(pszDst, CpQ);
1073 }
1074 else
1075 pszDst = RTStrPutCp(pszDst, Cp);
1076
1077 /* Need to leave space for terminating NUL. */
1078 if ((size_t)(pszDst - szBuf) + 1 >= cb)
1079 {
1080 rc = VERR_BUFFER_OVERFLOW;
1081 break;
1082 }
1083 memcpy(psz, szBuf, pszDst - szBuf);
1084 psz += pszDst - szBuf;
1085 }
1086 *psz = '\0';
1087 return rc;
1088}
1089
1090/**
1091 * Internal: free all buffers associated with grain directories.
1092 */
1093static void vmdkFreeGrainDirectory(PVMDKEXTENT pExtent)
1094{
1095 if (pExtent->pGD)
1096 {
1097 RTMemFree(pExtent->pGD);
1098 pExtent->pGD = NULL;
1099 }
1100 if (pExtent->pRGD)
1101 {
1102 RTMemFree(pExtent->pRGD);
1103 pExtent->pRGD = NULL;
1104 }
1105}
1106
1107/**
1108 * Internal: allocate the compressed/uncompressed buffers for streamOptimized
1109 * images.
1110 */
1111static int vmdkAllocStreamBuffers(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
1112{
1113 int rc = VINF_SUCCESS;
1114
1115 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
1116 {
1117 /* streamOptimized extents need a compressed grain buffer, which must
1118 * be big enough to hold uncompressible data (which needs ~8 bytes
1119 * more than the uncompressed data), the marker and padding. */
1120 pExtent->cbCompGrain = RT_ALIGN_Z( VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)
1121 + 8 + sizeof(VMDKMARKER), 512);
1122 pExtent->pvCompGrain = RTMemAlloc(pExtent->cbCompGrain);
1123 if (RT_LIKELY(pExtent->pvCompGrain))
1124 {
1125 /* streamOptimized extents need a decompressed grain buffer. */
1126 pExtent->pvGrain = RTMemAlloc(VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
1127 if (!pExtent->pvGrain)
1128 rc = VERR_NO_MEMORY;
1129 }
1130 else
1131 rc = VERR_NO_MEMORY;
1132 }
1133
1134 if (RT_FAILURE(rc))
1135 vmdkFreeStreamBuffers(pExtent);
1136 return rc;
1137}
1138
1139/**
1140 * Internal: allocate all buffers associated with grain directories.
1141 */
1142static int vmdkAllocGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
1143{
1144 RT_NOREF1(pImage);
1145 int rc = VINF_SUCCESS;
1146 size_t cbGD = pExtent->cGDEntries * sizeof(uint32_t);
1147
1148 pExtent->pGD = (uint32_t *)RTMemAllocZ(cbGD);
1149 if (RT_LIKELY(pExtent->pGD))
1150 {
1151 if (pExtent->uSectorRGD)
1152 {
1153 pExtent->pRGD = (uint32_t *)RTMemAllocZ(cbGD);
1154 if (RT_UNLIKELY(!pExtent->pRGD))
1155 rc = VERR_NO_MEMORY;
1156 }
1157 }
1158 else
1159 rc = VERR_NO_MEMORY;
1160
1161 if (RT_FAILURE(rc))
1162 vmdkFreeGrainDirectory(pExtent);
1163 return rc;
1164}
1165
1166/**
1167 * Converts the grain directory from little to host endianess.
1168 *
1169 * @param pGD The grain directory.
1170 * @param cGDEntries Number of entries in the grain directory to convert.
1171 */
1172DECLINLINE(void) vmdkGrainDirectoryConvToHost(uint32_t *pGD, uint32_t cGDEntries)
1173{
1174 uint32_t *pGDTmp = pGD;
1175
1176 for (uint32_t i = 0; i < cGDEntries; i++, pGDTmp++)
1177 *pGDTmp = RT_LE2H_U32(*pGDTmp);
1178}
1179
1180/**
1181 * Read the grain directory and allocated grain tables verifying them against
1182 * their back up copies if available.
1183 *
1184 * @returns VBox status code.
1185 * @param pImage Image instance data.
1186 * @param pExtent The VMDK extent.
1187 */
1188static int vmdkReadGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
1189{
1190 int rc = VINF_SUCCESS;
1191 size_t cbGD = pExtent->cGDEntries * sizeof(uint32_t);
1192
1193 AssertReturn(( pExtent->enmType == VMDKETYPE_HOSTED_SPARSE
1194 && pExtent->uSectorGD != VMDK_GD_AT_END
1195 && pExtent->uSectorRGD != VMDK_GD_AT_END), VERR_INTERNAL_ERROR);
1196
1197 rc = vmdkAllocGrainDirectory(pImage, pExtent);
1198 if (RT_SUCCESS(rc))
1199 {
1200 /* The VMDK 1.1 spec seems to talk about compressed grain directories,
1201 * but in reality they are not compressed. */
1202 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
1203 VMDK_SECTOR2BYTE(pExtent->uSectorGD),
1204 pExtent->pGD, cbGD);
1205 if (RT_SUCCESS(rc))
1206 {
1207 vmdkGrainDirectoryConvToHost(pExtent->pGD, pExtent->cGDEntries);
1208
1209 if ( pExtent->uSectorRGD
1210 && !(pImage->uOpenFlags & VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS))
1211 {
1212 /* The VMDK 1.1 spec seems to talk about compressed grain directories,
1213 * but in reality they are not compressed. */
1214 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
1215 VMDK_SECTOR2BYTE(pExtent->uSectorRGD),
1216 pExtent->pRGD, cbGD);
1217 if (RT_SUCCESS(rc))
1218 {
1219 vmdkGrainDirectoryConvToHost(pExtent->pRGD, pExtent->cGDEntries);
1220
1221 /* Check grain table and redundant grain table for consistency. */
1222 size_t cbGT = pExtent->cGTEntries * sizeof(uint32_t);
1223 size_t cbGTBuffers = cbGT; /* Start with space for one GT. */
1224 size_t cbGTBuffersMax = _1M;
1225
1226 uint32_t *pTmpGT1 = (uint32_t *)RTMemAlloc(cbGTBuffers);
1227 uint32_t *pTmpGT2 = (uint32_t *)RTMemAlloc(cbGTBuffers);
1228
1229 if ( !pTmpGT1
1230 || !pTmpGT2)
1231 rc = VERR_NO_MEMORY;
1232
1233 size_t i = 0;
1234 uint32_t *pGDTmp = pExtent->pGD;
1235 uint32_t *pRGDTmp = pExtent->pRGD;
1236
1237 /* Loop through all entries. */
1238 while (i < pExtent->cGDEntries)
1239 {
1240 uint32_t uGTStart = *pGDTmp;
1241 uint32_t uRGTStart = *pRGDTmp;
1242 size_t cbGTRead = cbGT;
1243
1244 /* If no grain table is allocated skip the entry. */
1245 if (*pGDTmp == 0 && *pRGDTmp == 0)
1246 {
1247 i++;
1248 continue;
1249 }
1250
1251 if (*pGDTmp == 0 || *pRGDTmp == 0 || *pGDTmp == *pRGDTmp)
1252 {
1253 /* Just one grain directory entry refers to a not yet allocated
1254 * grain table or both grain directory copies refer to the same
1255 * grain table. Not allowed. */
1256 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
1257 N_("VMDK: inconsistent references to grain directory in '%s'"), pExtent->pszFullname);
1258 break;
1259 }
1260
1261 i++;
1262 pGDTmp++;
1263 pRGDTmp++;
1264
1265 /*
1266 * Read a few tables at once if adjacent to decrease the number
1267 * of I/O requests. Read at maximum 1MB at once.
1268 */
1269 while ( i < pExtent->cGDEntries
1270 && cbGTRead < cbGTBuffersMax)
1271 {
1272 /* If no grain table is allocated skip the entry. */
1273 if (*pGDTmp == 0 && *pRGDTmp == 0)
1274 {
1275 i++;
1276 continue;
1277 }
1278
1279 if (*pGDTmp == 0 || *pRGDTmp == 0 || *pGDTmp == *pRGDTmp)
1280 {
1281 /* Just one grain directory entry refers to a not yet allocated
1282 * grain table or both grain directory copies refer to the same
1283 * grain table. Not allowed. */
1284 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
1285 N_("VMDK: inconsistent references to grain directory in '%s'"), pExtent->pszFullname);
1286 break;
1287 }
1288
1289 /* Check that the start offsets are adjacent.*/
1290 if ( VMDK_SECTOR2BYTE(uGTStart) + cbGTRead != VMDK_SECTOR2BYTE(*pGDTmp)
1291 || VMDK_SECTOR2BYTE(uRGTStart) + cbGTRead != VMDK_SECTOR2BYTE(*pRGDTmp))
1292 break;
1293
1294 i++;
1295 pGDTmp++;
1296 pRGDTmp++;
1297 cbGTRead += cbGT;
1298 }
1299
1300 /* Increase buffers if required. */
1301 if ( RT_SUCCESS(rc)
1302 && cbGTBuffers < cbGTRead)
1303 {
1304 uint32_t *pTmp;
1305 pTmp = (uint32_t *)RTMemRealloc(pTmpGT1, cbGTRead);
1306 if (pTmp)
1307 {
1308 pTmpGT1 = pTmp;
1309 pTmp = (uint32_t *)RTMemRealloc(pTmpGT2, cbGTRead);
1310 if (pTmp)
1311 pTmpGT2 = pTmp;
1312 else
1313 rc = VERR_NO_MEMORY;
1314 }
1315 else
1316 rc = VERR_NO_MEMORY;
1317
1318 if (rc == VERR_NO_MEMORY)
1319 {
1320 /* Reset to the old values. */
1321 rc = VINF_SUCCESS;
1322 i -= cbGTRead / cbGT;
1323 cbGTRead = cbGT;
1324
1325 /* Don't try to increase the buffer again in the next run. */
1326 cbGTBuffersMax = cbGTBuffers;
1327 }
1328 }
1329
1330 if (RT_SUCCESS(rc))
1331 {
1332 /* The VMDK 1.1 spec seems to talk about compressed grain tables,
1333 * but in reality they are not compressed. */
1334 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
1335 VMDK_SECTOR2BYTE(uGTStart),
1336 pTmpGT1, cbGTRead);
1337 if (RT_FAILURE(rc))
1338 {
1339 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1340 N_("VMDK: error reading grain table in '%s'"), pExtent->pszFullname);
1341 break;
1342 }
1343 /* The VMDK 1.1 spec seems to talk about compressed grain tables,
1344 * but in reality they are not compressed. */
1345 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
1346 VMDK_SECTOR2BYTE(uRGTStart),
1347 pTmpGT2, cbGTRead);
1348 if (RT_FAILURE(rc))
1349 {
1350 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1351 N_("VMDK: error reading backup grain table in '%s'"), pExtent->pszFullname);
1352 break;
1353 }
1354 if (memcmp(pTmpGT1, pTmpGT2, cbGTRead))
1355 {
1356 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
1357 N_("VMDK: inconsistency between grain table and backup grain table in '%s'"), pExtent->pszFullname);
1358 break;
1359 }
1360 }
1361 } /* while (i < pExtent->cGDEntries) */
1362
1363 /** @todo figure out what to do for unclean VMDKs. */
1364 if (pTmpGT1)
1365 RTMemFree(pTmpGT1);
1366 if (pTmpGT2)
1367 RTMemFree(pTmpGT2);
1368 }
1369 else
1370 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1371 N_("VMDK: could not read redundant grain directory in '%s'"), pExtent->pszFullname);
1372 }
1373 }
1374 else
1375 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
1376 N_("VMDK: could not read grain directory in '%s': %Rrc"), pExtent->pszFullname, rc);
1377 }
1378
1379 if (RT_FAILURE(rc))
1380 vmdkFreeGrainDirectory(pExtent);
1381 return rc;
1382}
1383
1384/**
1385 * Creates a new grain directory for the given extent at the given start sector.
1386 *
1387 * @returns VBox status code.
1388 * @param pImage Image instance data.
1389 * @param pExtent The VMDK extent.
1390 * @param uStartSector Where the grain directory should be stored in the image.
1391 * @param fPreAlloc Flag whether to pre allocate the grain tables at this point.
1392 */
1393static int vmdkCreateGrainDirectory(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
1394 uint64_t uStartSector, bool fPreAlloc)
1395{
1396 int rc = VINF_SUCCESS;
1397 unsigned i;
1398 size_t cbGD = pExtent->cGDEntries * sizeof(uint32_t);
1399 size_t cbGDRounded = RT_ALIGN_64(cbGD, 512);
1400 size_t cbGTRounded;
1401 uint64_t cbOverhead;
1402
1403 if (fPreAlloc)
1404 {
1405 cbGTRounded = RT_ALIGN_64(pExtent->cGDEntries * pExtent->cGTEntries * sizeof(uint32_t), 512);
1406 cbOverhead = VMDK_SECTOR2BYTE(uStartSector) + cbGDRounded + cbGTRounded;
1407 }
1408 else
1409 {
1410 /* Use a dummy start sector for layout computation. */
1411 if (uStartSector == VMDK_GD_AT_END)
1412 uStartSector = 1;
1413 cbGTRounded = 0;
1414 cbOverhead = VMDK_SECTOR2BYTE(uStartSector) + cbGDRounded;
1415 }
1416
1417 /* For streamOptimized extents there is only one grain directory,
1418 * and for all others take redundant grain directory into account. */
1419 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
1420 {
1421 cbOverhead = RT_ALIGN_64(cbOverhead,
1422 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
1423 }
1424 else
1425 {
1426 cbOverhead += cbGDRounded + cbGTRounded;
1427 cbOverhead = RT_ALIGN_64(cbOverhead,
1428 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
1429 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pExtent->pFile->pStorage, cbOverhead);
1430 }
1431
1432 if (RT_SUCCESS(rc))
1433 {
1434 pExtent->uAppendPosition = cbOverhead;
1435 pExtent->cOverheadSectors = VMDK_BYTE2SECTOR(cbOverhead);
1436
1437 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
1438 {
1439 pExtent->uSectorRGD = 0;
1440 pExtent->uSectorGD = uStartSector;
1441 }
1442 else
1443 {
1444 pExtent->uSectorRGD = uStartSector;
1445 pExtent->uSectorGD = uStartSector + VMDK_BYTE2SECTOR(cbGDRounded + cbGTRounded);
1446 }
1447
1448 rc = vmdkAllocStreamBuffers(pImage, pExtent);
1449 if (RT_SUCCESS(rc))
1450 {
1451 rc = vmdkAllocGrainDirectory(pImage, pExtent);
1452 if ( RT_SUCCESS(rc)
1453 && fPreAlloc)
1454 {
1455 uint32_t uGTSectorLE;
1456 uint64_t uOffsetSectors;
1457
1458 if (pExtent->pRGD)
1459 {
1460 uOffsetSectors = pExtent->uSectorRGD + VMDK_BYTE2SECTOR(cbGDRounded);
1461 for (i = 0; i < pExtent->cGDEntries; i++)
1462 {
1463 pExtent->pRGD[i] = uOffsetSectors;
1464 uGTSectorLE = RT_H2LE_U64(uOffsetSectors);
1465 /* Write the redundant grain directory entry to disk. */
1466 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
1467 VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + i * sizeof(uGTSectorLE),
1468 &uGTSectorLE, sizeof(uGTSectorLE));
1469 if (RT_FAILURE(rc))
1470 {
1471 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write new redundant grain directory entry in '%s'"), pExtent->pszFullname);
1472 break;
1473 }
1474 uOffsetSectors += VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t));
1475 }
1476 }
1477
1478 if (RT_SUCCESS(rc))
1479 {
1480 uOffsetSectors = pExtent->uSectorGD + VMDK_BYTE2SECTOR(cbGDRounded);
1481 for (i = 0; i < pExtent->cGDEntries; i++)
1482 {
1483 pExtent->pGD[i] = uOffsetSectors;
1484 uGTSectorLE = RT_H2LE_U64(uOffsetSectors);
1485 /* Write the grain directory entry to disk. */
1486 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
1487 VMDK_SECTOR2BYTE(pExtent->uSectorGD) + i * sizeof(uGTSectorLE),
1488 &uGTSectorLE, sizeof(uGTSectorLE));
1489 if (RT_FAILURE(rc))
1490 {
1491 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write new grain directory entry in '%s'"), pExtent->pszFullname);
1492 break;
1493 }
1494 uOffsetSectors += VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t));
1495 }
1496 }
1497 }
1498 }
1499 }
1500
1501 if (RT_FAILURE(rc))
1502 vmdkFreeGrainDirectory(pExtent);
1503 return rc;
1504}
1505
1506/**
1507 * Unquotes the given string returning the result in a separate buffer.
1508 *
1509 * @returns VBox status code.
1510 * @param pImage The VMDK image state.
1511 * @param pszStr The string to unquote.
1512 * @param ppszUnquoted Where to store the return value, use RTMemTmpFree to
1513 * free.
1514 * @param ppszNext Where to store the pointer to any character following
1515 * the quoted value, optional.
1516 */
1517static int vmdkStringUnquote(PVMDKIMAGE pImage, const char *pszStr,
1518 char **ppszUnquoted, char **ppszNext)
1519{
1520 const char *pszStart = pszStr;
1521 char *pszQ;
1522 char *pszUnquoted;
1523
1524 /* Skip over whitespace. */
1525 while (*pszStr == ' ' || *pszStr == '\t')
1526 pszStr++;
1527
1528 if (*pszStr != '"')
1529 {
1530 pszQ = (char *)pszStr;
1531 while (*pszQ && *pszQ != ' ' && *pszQ != '\t')
1532 pszQ++;
1533 }
1534 else
1535 {
1536 pszStr++;
1537 pszQ = (char *)strchr(pszStr, '"');
1538 if (pszQ == NULL)
1539 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrectly quoted value in descriptor in '%s' (raw value %s)"),
1540 pImage->pszFilename, pszStart);
1541 }
1542
1543 pszUnquoted = (char *)RTMemTmpAlloc(pszQ - pszStr + 1);
1544 if (!pszUnquoted)
1545 return VERR_NO_MEMORY;
1546 memcpy(pszUnquoted, pszStr, pszQ - pszStr);
1547 pszUnquoted[pszQ - pszStr] = '\0';
1548 *ppszUnquoted = pszUnquoted;
1549 if (ppszNext)
1550 *ppszNext = pszQ + 1;
1551 return VINF_SUCCESS;
1552}
1553
1554static int vmdkDescInitStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
1555 const char *pszLine)
1556{
1557 char *pEnd = pDescriptor->aLines[pDescriptor->cLines];
1558 ssize_t cbDiff = strlen(pszLine) + 1;
1559
1560 if ( pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1
1561 && pEnd - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff)
1562 return vdIfError(pImage->pIfError, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
1563
1564 memcpy(pEnd, pszLine, cbDiff);
1565 pDescriptor->cLines++;
1566 pDescriptor->aLines[pDescriptor->cLines] = pEnd + cbDiff;
1567 pDescriptor->fDirty = true;
1568
1569 return VINF_SUCCESS;
1570}
1571
1572static bool vmdkDescGetStr(PVMDKDESCRIPTOR pDescriptor, unsigned uStart,
1573 const char *pszKey, const char **ppszValue)
1574{
1575 size_t cbKey = strlen(pszKey);
1576 const char *pszValue;
1577
1578 while (uStart != 0)
1579 {
1580 if (!strncmp(pDescriptor->aLines[uStart], pszKey, cbKey))
1581 {
1582 /* Key matches, check for a '=' (preceded by whitespace). */
1583 pszValue = pDescriptor->aLines[uStart] + cbKey;
1584 while (*pszValue == ' ' || *pszValue == '\t')
1585 pszValue++;
1586 if (*pszValue == '=')
1587 {
1588 *ppszValue = pszValue + 1;
1589 break;
1590 }
1591 }
1592 uStart = pDescriptor->aNextLines[uStart];
1593 }
1594 return !!uStart;
1595}
1596
1597static int vmdkDescSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
1598 unsigned uStart,
1599 const char *pszKey, const char *pszValue)
1600{
1601 char *pszTmp = NULL; /* (MSC naturally cannot figure this isn't used uninitialized) */
1602 size_t cbKey = strlen(pszKey);
1603 unsigned uLast = 0;
1604
1605 while (uStart != 0)
1606 {
1607 if (!strncmp(pDescriptor->aLines[uStart], pszKey, cbKey))
1608 {
1609 /* Key matches, check for a '=' (preceded by whitespace). */
1610 pszTmp = pDescriptor->aLines[uStart] + cbKey;
1611 while (*pszTmp == ' ' || *pszTmp == '\t')
1612 pszTmp++;
1613 if (*pszTmp == '=')
1614 {
1615 pszTmp++;
1616 /** @todo r=bird: Doesn't skipping trailing blanks here just cause unecessary
1617 * bloat and potentially out of space error? */
1618 while (*pszTmp == ' ' || *pszTmp == '\t')
1619 pszTmp++;
1620 break;
1621 }
1622 }
1623 if (!pDescriptor->aNextLines[uStart])
1624 uLast = uStart;
1625 uStart = pDescriptor->aNextLines[uStart];
1626 }
1627 if (uStart)
1628 {
1629 if (pszValue)
1630 {
1631 /* Key already exists, replace existing value. */
1632 size_t cbOldVal = strlen(pszTmp);
1633 size_t cbNewVal = strlen(pszValue);
1634 ssize_t cbDiff = cbNewVal - cbOldVal;
1635 /* Check for buffer overflow. */
1636 if ( pDescriptor->aLines[pDescriptor->cLines] - pDescriptor->aLines[0]
1637 > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff)
1638 return vdIfError(pImage->pIfError, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
1639
1640 memmove(pszTmp + cbNewVal, pszTmp + cbOldVal,
1641 pDescriptor->aLines[pDescriptor->cLines] - pszTmp - cbOldVal);
1642 memcpy(pszTmp, pszValue, cbNewVal + 1);
1643 for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
1644 pDescriptor->aLines[i] += cbDiff;
1645 }
1646 else
1647 {
1648 memmove(pDescriptor->aLines[uStart], pDescriptor->aLines[uStart+1],
1649 pDescriptor->aLines[pDescriptor->cLines] - pDescriptor->aLines[uStart+1] + 1);
1650 for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
1651 {
1652 pDescriptor->aLines[i-1] = pDescriptor->aLines[i];
1653 if (pDescriptor->aNextLines[i])
1654 pDescriptor->aNextLines[i-1] = pDescriptor->aNextLines[i] - 1;
1655 else
1656 pDescriptor->aNextLines[i-1] = 0;
1657 }
1658 pDescriptor->cLines--;
1659 /* Adjust starting line numbers of following descriptor sections. */
1660 if (uStart < pDescriptor->uFirstExtent)
1661 pDescriptor->uFirstExtent--;
1662 if (uStart < pDescriptor->uFirstDDB)
1663 pDescriptor->uFirstDDB--;
1664 }
1665 }
1666 else
1667 {
1668 /* Key doesn't exist, append after the last entry in this category. */
1669 if (!pszValue)
1670 {
1671 /* Key doesn't exist, and it should be removed. Simply a no-op. */
1672 return VINF_SUCCESS;
1673 }
1674 cbKey = strlen(pszKey);
1675 size_t cbValue = strlen(pszValue);
1676 ssize_t cbDiff = cbKey + 1 + cbValue + 1;
1677 /* Check for buffer overflow. */
1678 if ( (pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1)
1679 || ( pDescriptor->aLines[pDescriptor->cLines]
1680 - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff))
1681 return vdIfError(pImage->pIfError, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
1682 for (unsigned i = pDescriptor->cLines + 1; i > uLast + 1; i--)
1683 {
1684 pDescriptor->aLines[i] = pDescriptor->aLines[i - 1];
1685 if (pDescriptor->aNextLines[i - 1])
1686 pDescriptor->aNextLines[i] = pDescriptor->aNextLines[i - 1] + 1;
1687 else
1688 pDescriptor->aNextLines[i] = 0;
1689 }
1690 uStart = uLast + 1;
1691 pDescriptor->aNextLines[uLast] = uStart;
1692 pDescriptor->aNextLines[uStart] = 0;
1693 pDescriptor->cLines++;
1694 pszTmp = pDescriptor->aLines[uStart];
1695 memmove(pszTmp + cbDiff, pszTmp,
1696 pDescriptor->aLines[pDescriptor->cLines] - pszTmp);
1697 memcpy(pDescriptor->aLines[uStart], pszKey, cbKey);
1698 pDescriptor->aLines[uStart][cbKey] = '=';
1699 memcpy(pDescriptor->aLines[uStart] + cbKey + 1, pszValue, cbValue + 1);
1700 for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
1701 pDescriptor->aLines[i] += cbDiff;
1702
1703 /* Adjust starting line numbers of following descriptor sections. */
1704 if (uStart <= pDescriptor->uFirstExtent)
1705 pDescriptor->uFirstExtent++;
1706 if (uStart <= pDescriptor->uFirstDDB)
1707 pDescriptor->uFirstDDB++;
1708 }
1709 pDescriptor->fDirty = true;
1710 return VINF_SUCCESS;
1711}
1712
1713static int vmdkDescBaseGetU32(PVMDKDESCRIPTOR pDescriptor, const char *pszKey,
1714 uint32_t *puValue)
1715{
1716 const char *pszValue;
1717
1718 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDesc, pszKey,
1719 &pszValue))
1720 return VERR_VD_VMDK_VALUE_NOT_FOUND;
1721 return RTStrToUInt32Ex(pszValue, NULL, 10, puValue);
1722}
1723
1724/**
1725 * Returns the value of the given key as a string allocating the necessary memory.
1726 *
1727 * @returns VBox status code.
1728 * @retval VERR_VD_VMDK_VALUE_NOT_FOUND if the value could not be found.
1729 * @param pImage The VMDK image state.
1730 * @param pDescriptor The descriptor to fetch the value from.
1731 * @param pszKey The key to get the value from.
1732 * @param ppszValue Where to store the return value, use RTMemTmpFree to
1733 * free.
1734 */
1735static int vmdkDescBaseGetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
1736 const char *pszKey, char **ppszValue)
1737{
1738 const char *pszValue;
1739 char *pszValueUnquoted;
1740
1741 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDesc, pszKey,
1742 &pszValue))
1743 return VERR_VD_VMDK_VALUE_NOT_FOUND;
1744 int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
1745 if (RT_FAILURE(rc))
1746 return rc;
1747 *ppszValue = pszValueUnquoted;
1748 return rc;
1749}
1750
1751static int vmdkDescBaseSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
1752 const char *pszKey, const char *pszValue)
1753{
1754 char *pszValueQuoted;
1755
1756 RTStrAPrintf(&pszValueQuoted, "\"%s\"", pszValue);
1757 if (!pszValueQuoted)
1758 return VERR_NO_STR_MEMORY;
1759 int rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc, pszKey,
1760 pszValueQuoted);
1761 RTStrFree(pszValueQuoted);
1762 return rc;
1763}
1764
1765static void vmdkDescExtRemoveDummy(PVMDKIMAGE pImage,
1766 PVMDKDESCRIPTOR pDescriptor)
1767{
1768 RT_NOREF1(pImage);
1769 unsigned uEntry = pDescriptor->uFirstExtent;
1770 ssize_t cbDiff;
1771
1772 if (!uEntry)
1773 return;
1774
1775 cbDiff = strlen(pDescriptor->aLines[uEntry]) + 1;
1776 /* Move everything including \0 in the entry marking the end of buffer. */
1777 memmove(pDescriptor->aLines[uEntry], pDescriptor->aLines[uEntry + 1],
1778 pDescriptor->aLines[pDescriptor->cLines] - pDescriptor->aLines[uEntry + 1] + 1);
1779 for (unsigned i = uEntry + 1; i <= pDescriptor->cLines; i++)
1780 {
1781 pDescriptor->aLines[i - 1] = pDescriptor->aLines[i] - cbDiff;
1782 if (pDescriptor->aNextLines[i])
1783 pDescriptor->aNextLines[i - 1] = pDescriptor->aNextLines[i] - 1;
1784 else
1785 pDescriptor->aNextLines[i - 1] = 0;
1786 }
1787 pDescriptor->cLines--;
1788 if (pDescriptor->uFirstDDB)
1789 pDescriptor->uFirstDDB--;
1790
1791 return;
1792}
1793
1794static void vmdkDescExtRemoveByLine(PVMDKIMAGE pImage,
1795 PVMDKDESCRIPTOR pDescriptor, unsigned uLine)
1796{
1797 RT_NOREF1(pImage);
1798 unsigned uEntry = uLine;
1799 ssize_t cbDiff;
1800 if (!uEntry)
1801 return;
1802 cbDiff = strlen(pDescriptor->aLines[uEntry]) + 1;
1803 /* Move everything including \0 in the entry marking the end of buffer. */
1804 memmove(pDescriptor->aLines[uEntry], pDescriptor->aLines[uEntry + 1],
1805 pDescriptor->aLines[pDescriptor->cLines] - pDescriptor->aLines[uEntry + 1] + 1);
1806 for (unsigned i = uEntry; i <= pDescriptor->cLines; i++)
1807 {
1808 if (i != uEntry)
1809 pDescriptor->aLines[i - 1] = pDescriptor->aLines[i] - cbDiff;
1810 if (pDescriptor->aNextLines[i])
1811 pDescriptor->aNextLines[i - 1] = pDescriptor->aNextLines[i] - 1;
1812 else
1813 pDescriptor->aNextLines[i - 1] = 0;
1814 }
1815 pDescriptor->cLines--;
1816 if (pDescriptor->uFirstDDB)
1817 pDescriptor->uFirstDDB--;
1818 return;
1819}
1820
1821static int vmdkDescExtInsert(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
1822 VMDKACCESS enmAccess, uint64_t cNominalSectors,
1823 VMDKETYPE enmType, const char *pszBasename,
1824 uint64_t uSectorOffset)
1825{
1826 static const char *apszAccess[] = { "NOACCESS", "RDONLY", "RW" };
1827 static const char *apszType[] = { "", "SPARSE", "FLAT", "ZERO", "VMFS" };
1828 char *pszTmp;
1829 unsigned uStart = pDescriptor->uFirstExtent, uLast = 0;
1830 char szExt[1024];
1831 ssize_t cbDiff;
1832
1833 Assert((unsigned)enmAccess < RT_ELEMENTS(apszAccess));
1834 Assert((unsigned)enmType < RT_ELEMENTS(apszType));
1835
1836 /* Find last entry in extent description. */
1837 while (uStart)
1838 {
1839 if (!pDescriptor->aNextLines[uStart])
1840 uLast = uStart;
1841 uStart = pDescriptor->aNextLines[uStart];
1842 }
1843
1844 if (enmType == VMDKETYPE_ZERO)
1845 {
1846 RTStrPrintf(szExt, sizeof(szExt), "%s %llu %s ", apszAccess[enmAccess],
1847 cNominalSectors, apszType[enmType]);
1848 }
1849 else if (enmType == VMDKETYPE_FLAT)
1850 {
1851 RTStrPrintf(szExt, sizeof(szExt), "%s %llu %s \"%s\" %llu",
1852 apszAccess[enmAccess], cNominalSectors,
1853 apszType[enmType], pszBasename, uSectorOffset);
1854 }
1855 else
1856 {
1857 RTStrPrintf(szExt, sizeof(szExt), "%s %llu %s \"%s\"",
1858 apszAccess[enmAccess], cNominalSectors,
1859 apszType[enmType], pszBasename);
1860 }
1861 cbDiff = strlen(szExt) + 1;
1862
1863 /* Check for buffer overflow. */
1864 if ( (pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1)
1865 || ( pDescriptor->aLines[pDescriptor->cLines]
1866 - pDescriptor->aLines[0] > (ptrdiff_t)pDescriptor->cbDescAlloc - cbDiff))
1867 {
1868 if ((pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
1869 && !(pDescriptor->cLines >= VMDK_DESCRIPTOR_LINES_MAX - 1))
1870 {
1871 pImage->cbDescAlloc *= 2;
1872 pDescriptor->cbDescAlloc *= 2;
1873 }
1874 else
1875 return vdIfError(pImage->pIfError, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
1876 }
1877
1878 for (unsigned i = pDescriptor->cLines + 1; i > uLast + 1; i--)
1879 {
1880 pDescriptor->aLines[i] = pDescriptor->aLines[i - 1];
1881 if (pDescriptor->aNextLines[i - 1])
1882 pDescriptor->aNextLines[i] = pDescriptor->aNextLines[i - 1] + 1;
1883 else
1884 pDescriptor->aNextLines[i] = 0;
1885 }
1886 uStart = uLast + 1;
1887 pDescriptor->aNextLines[uLast] = uStart;
1888 pDescriptor->aNextLines[uStart] = 0;
1889 pDescriptor->cLines++;
1890 pszTmp = pDescriptor->aLines[uStart];
1891 memmove(pszTmp + cbDiff, pszTmp,
1892 pDescriptor->aLines[pDescriptor->cLines] - pszTmp);
1893 memcpy(pDescriptor->aLines[uStart], szExt, cbDiff);
1894 for (unsigned i = uStart + 1; i <= pDescriptor->cLines; i++)
1895 pDescriptor->aLines[i] += cbDiff;
1896
1897 /* Adjust starting line numbers of following descriptor sections. */
1898 if (uStart <= pDescriptor->uFirstDDB)
1899 pDescriptor->uFirstDDB++;
1900
1901 pDescriptor->fDirty = true;
1902 return VINF_SUCCESS;
1903}
1904
1905/**
1906 * Returns the value of the given key from the DDB as a string allocating
1907 * the necessary memory.
1908 *
1909 * @returns VBox status code.
1910 * @retval VERR_VD_VMDK_VALUE_NOT_FOUND if the value could not be found.
1911 * @param pImage The VMDK image state.
1912 * @param pDescriptor The descriptor to fetch the value from.
1913 * @param pszKey The key to get the value from.
1914 * @param ppszValue Where to store the return value, use RTMemTmpFree to
1915 * free.
1916 */
1917static int vmdkDescDDBGetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
1918 const char *pszKey, char **ppszValue)
1919{
1920 const char *pszValue;
1921 char *pszValueUnquoted;
1922
1923 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey,
1924 &pszValue))
1925 return VERR_VD_VMDK_VALUE_NOT_FOUND;
1926 int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
1927 if (RT_FAILURE(rc))
1928 return rc;
1929 *ppszValue = pszValueUnquoted;
1930 return rc;
1931}
1932
1933static int vmdkDescDDBGetU32(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
1934 const char *pszKey, uint32_t *puValue)
1935{
1936 const char *pszValue;
1937 char *pszValueUnquoted;
1938
1939 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey,
1940 &pszValue))
1941 return VERR_VD_VMDK_VALUE_NOT_FOUND;
1942 int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
1943 if (RT_FAILURE(rc))
1944 return rc;
1945 rc = RTStrToUInt32Ex(pszValueUnquoted, NULL, 10, puValue);
1946 RTMemTmpFree(pszValueUnquoted);
1947 return rc;
1948}
1949
1950static int vmdkDescDDBGetUuid(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
1951 const char *pszKey, PRTUUID pUuid)
1952{
1953 const char *pszValue;
1954 char *pszValueUnquoted;
1955
1956 if (!vmdkDescGetStr(pDescriptor, pDescriptor->uFirstDDB, pszKey,
1957 &pszValue))
1958 return VERR_VD_VMDK_VALUE_NOT_FOUND;
1959 int rc = vmdkStringUnquote(pImage, pszValue, &pszValueUnquoted, NULL);
1960 if (RT_FAILURE(rc))
1961 return rc;
1962 rc = RTUuidFromStr(pUuid, pszValueUnquoted);
1963 RTMemTmpFree(pszValueUnquoted);
1964 return rc;
1965}
1966
1967static int vmdkDescDDBSetStr(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
1968 const char *pszKey, const char *pszVal)
1969{
1970 int rc;
1971 char *pszValQuoted;
1972
1973 if (pszVal)
1974 {
1975 RTStrAPrintf(&pszValQuoted, "\"%s\"", pszVal);
1976 if (!pszValQuoted)
1977 return VERR_NO_STR_MEMORY;
1978 }
1979 else
1980 pszValQuoted = NULL;
1981 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey,
1982 pszValQuoted);
1983 if (pszValQuoted)
1984 RTStrFree(pszValQuoted);
1985 return rc;
1986}
1987
1988static int vmdkDescDDBSetUuid(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
1989 const char *pszKey, PCRTUUID pUuid)
1990{
1991 char *pszUuid;
1992
1993 RTStrAPrintf(&pszUuid, "\"%RTuuid\"", pUuid);
1994 if (!pszUuid)
1995 return VERR_NO_STR_MEMORY;
1996 int rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey,
1997 pszUuid);
1998 RTStrFree(pszUuid);
1999 return rc;
2000}
2001
2002static int vmdkDescDDBSetU32(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDescriptor,
2003 const char *pszKey, uint32_t uValue)
2004{
2005 char *pszValue;
2006
2007 RTStrAPrintf(&pszValue, "\"%d\"", uValue);
2008 if (!pszValue)
2009 return VERR_NO_STR_MEMORY;
2010 int rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDDB, pszKey,
2011 pszValue);
2012 RTStrFree(pszValue);
2013 return rc;
2014}
2015
2016/**
2017 * Splits the descriptor data into individual lines checking for correct line
2018 * endings and descriptor size.
2019 *
2020 * @returns VBox status code.
2021 * @param pImage The image instance.
2022 * @param pDesc The descriptor.
2023 * @param pszTmp The raw descriptor data from the image.
2024 */
2025static int vmdkDescSplitLines(PVMDKIMAGE pImage, PVMDKDESCRIPTOR pDesc, char *pszTmp)
2026{
2027 unsigned cLine = 0;
2028 int rc = VINF_SUCCESS;
2029
2030 while ( RT_SUCCESS(rc)
2031 && *pszTmp != '\0')
2032 {
2033 pDesc->aLines[cLine++] = pszTmp;
2034 if (cLine >= VMDK_DESCRIPTOR_LINES_MAX)
2035 {
2036 vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: descriptor too big in '%s'"), pImage->pszFilename);
2037 rc = VERR_VD_VMDK_INVALID_HEADER;
2038 break;
2039 }
2040
2041 while (*pszTmp != '\0' && *pszTmp != '\n')
2042 {
2043 if (*pszTmp == '\r')
2044 {
2045 if (*(pszTmp + 1) != '\n')
2046 {
2047 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: unsupported end of line in descriptor in '%s'"), pImage->pszFilename);
2048 break;
2049 }
2050 else
2051 {
2052 /* Get rid of CR character. */
2053 *pszTmp = '\0';
2054 }
2055 }
2056 pszTmp++;
2057 }
2058
2059 if (RT_FAILURE(rc))
2060 break;
2061
2062 /* Get rid of LF character. */
2063 if (*pszTmp == '\n')
2064 {
2065 *pszTmp = '\0';
2066 pszTmp++;
2067 }
2068 }
2069
2070 if (RT_SUCCESS(rc))
2071 {
2072 pDesc->cLines = cLine;
2073 /* Pointer right after the end of the used part of the buffer. */
2074 pDesc->aLines[cLine] = pszTmp;
2075 }
2076
2077 return rc;
2078}
2079
2080static int vmdkPreprocessDescriptor(PVMDKIMAGE pImage, char *pDescData,
2081 size_t cbDescData, PVMDKDESCRIPTOR pDescriptor)
2082{
2083 pDescriptor->cbDescAlloc = cbDescData;
2084 int rc = vmdkDescSplitLines(pImage, pDescriptor, pDescData);
2085 if (RT_SUCCESS(rc))
2086 {
2087 if ( strcmp(pDescriptor->aLines[0], "# Disk DescriptorFile")
2088 && strcmp(pDescriptor->aLines[0], "# Disk Descriptor File")
2089 && strcmp(pDescriptor->aLines[0], "#Disk Descriptor File")
2090 && strcmp(pDescriptor->aLines[0], "#Disk DescriptorFile"))
2091 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2092 N_("VMDK: descriptor does not start as expected in '%s'"), pImage->pszFilename);
2093 else
2094 {
2095 unsigned uLastNonEmptyLine = 0;
2096
2097 /* Initialize those, because we need to be able to reopen an image. */
2098 pDescriptor->uFirstDesc = 0;
2099 pDescriptor->uFirstExtent = 0;
2100 pDescriptor->uFirstDDB = 0;
2101 for (unsigned i = 0; i < pDescriptor->cLines; i++)
2102 {
2103 if (*pDescriptor->aLines[i] != '#' && *pDescriptor->aLines[i] != '\0')
2104 {
2105 if ( !strncmp(pDescriptor->aLines[i], "RW", 2)
2106 || !strncmp(pDescriptor->aLines[i], "RDONLY", 6)
2107 || !strncmp(pDescriptor->aLines[i], "NOACCESS", 8) )
2108 {
2109 /* An extent descriptor. */
2110 if (!pDescriptor->uFirstDesc || pDescriptor->uFirstDDB)
2111 {
2112 /* Incorrect ordering of entries. */
2113 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2114 N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);
2115 break;
2116 }
2117 if (!pDescriptor->uFirstExtent)
2118 {
2119 pDescriptor->uFirstExtent = i;
2120 uLastNonEmptyLine = 0;
2121 }
2122 }
2123 else if (!strncmp(pDescriptor->aLines[i], "ddb.", 4))
2124 {
2125 /* A disk database entry. */
2126 if (!pDescriptor->uFirstDesc || !pDescriptor->uFirstExtent)
2127 {
2128 /* Incorrect ordering of entries. */
2129 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2130 N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);
2131 break;
2132 }
2133 if (!pDescriptor->uFirstDDB)
2134 {
2135 pDescriptor->uFirstDDB = i;
2136 uLastNonEmptyLine = 0;
2137 }
2138 }
2139 else
2140 {
2141 /* A normal entry. */
2142 if (pDescriptor->uFirstExtent || pDescriptor->uFirstDDB)
2143 {
2144 /* Incorrect ordering of entries. */
2145 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2146 N_("VMDK: incorrect ordering of entries in descriptor in '%s'"), pImage->pszFilename);
2147 break;
2148 }
2149 if (!pDescriptor->uFirstDesc)
2150 {
2151 pDescriptor->uFirstDesc = i;
2152 uLastNonEmptyLine = 0;
2153 }
2154 }
2155 if (uLastNonEmptyLine)
2156 pDescriptor->aNextLines[uLastNonEmptyLine] = i;
2157 uLastNonEmptyLine = i;
2158 }
2159 }
2160 }
2161 }
2162
2163 return rc;
2164}
2165
2166static int vmdkDescSetPCHSGeometry(PVMDKIMAGE pImage,
2167 PCVDGEOMETRY pPCHSGeometry)
2168{
2169 int rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2170 VMDK_DDB_GEO_PCHS_CYLINDERS,
2171 pPCHSGeometry->cCylinders);
2172 if (RT_FAILURE(rc))
2173 return rc;
2174 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2175 VMDK_DDB_GEO_PCHS_HEADS,
2176 pPCHSGeometry->cHeads);
2177 if (RT_FAILURE(rc))
2178 return rc;
2179 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2180 VMDK_DDB_GEO_PCHS_SECTORS,
2181 pPCHSGeometry->cSectors);
2182 return rc;
2183}
2184
2185static int vmdkDescSetLCHSGeometry(PVMDKIMAGE pImage,
2186 PCVDGEOMETRY pLCHSGeometry)
2187{
2188 int rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2189 VMDK_DDB_GEO_LCHS_CYLINDERS,
2190 pLCHSGeometry->cCylinders);
2191 if (RT_FAILURE(rc))
2192 return rc;
2193 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2194 VMDK_DDB_GEO_LCHS_HEADS,
2195
2196 pLCHSGeometry->cHeads);
2197 if (RT_FAILURE(rc))
2198 return rc;
2199 rc = vmdkDescDDBSetU32(pImage, &pImage->Descriptor,
2200 VMDK_DDB_GEO_LCHS_SECTORS,
2201 pLCHSGeometry->cSectors);
2202 return rc;
2203}
2204
2205static int vmdkCreateDescriptor(PVMDKIMAGE pImage, char *pDescData,
2206 size_t cbDescData, PVMDKDESCRIPTOR pDescriptor)
2207{
2208 pDescriptor->uFirstDesc = 0;
2209 pDescriptor->uFirstExtent = 0;
2210 pDescriptor->uFirstDDB = 0;
2211 pDescriptor->cLines = 0;
2212 pDescriptor->cbDescAlloc = cbDescData;
2213 pDescriptor->fDirty = false;
2214 pDescriptor->aLines[pDescriptor->cLines] = pDescData;
2215 memset(pDescriptor->aNextLines, '\0', sizeof(pDescriptor->aNextLines));
2216
2217 int rc = vmdkDescInitStr(pImage, pDescriptor, "# Disk DescriptorFile");
2218 if (RT_SUCCESS(rc))
2219 rc = vmdkDescInitStr(pImage, pDescriptor, "version=1");
2220 if (RT_SUCCESS(rc))
2221 {
2222 pDescriptor->uFirstDesc = pDescriptor->cLines - 1;
2223 rc = vmdkDescInitStr(pImage, pDescriptor, "");
2224 }
2225 if (RT_SUCCESS(rc))
2226 rc = vmdkDescInitStr(pImage, pDescriptor, "# Extent description");
2227 if (RT_SUCCESS(rc))
2228 rc = vmdkDescInitStr(pImage, pDescriptor, "NOACCESS 0 ZERO ");
2229 if (RT_SUCCESS(rc))
2230 {
2231 pDescriptor->uFirstExtent = pDescriptor->cLines - 1;
2232 rc = vmdkDescInitStr(pImage, pDescriptor, "");
2233 }
2234 if (RT_SUCCESS(rc))
2235 {
2236 /* The trailing space is created by VMware, too. */
2237 rc = vmdkDescInitStr(pImage, pDescriptor, "# The disk Data Base ");
2238 }
2239 if (RT_SUCCESS(rc))
2240 rc = vmdkDescInitStr(pImage, pDescriptor, "#DDB");
2241 if (RT_SUCCESS(rc))
2242 rc = vmdkDescInitStr(pImage, pDescriptor, "");
2243 if (RT_SUCCESS(rc))
2244 rc = vmdkDescInitStr(pImage, pDescriptor, "ddb.virtualHWVersion = \"4\"");
2245 if (RT_SUCCESS(rc))
2246 {
2247 pDescriptor->uFirstDDB = pDescriptor->cLines - 1;
2248
2249 /* Now that the framework is in place, use the normal functions to insert
2250 * the remaining keys. */
2251 char szBuf[9];
2252 RTStrPrintf(szBuf, sizeof(szBuf), "%08x", RTRandU32());
2253 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc,
2254 "CID", szBuf);
2255 }
2256 if (RT_SUCCESS(rc))
2257 rc = vmdkDescSetStr(pImage, pDescriptor, pDescriptor->uFirstDesc,
2258 "parentCID", "ffffffff");
2259 if (RT_SUCCESS(rc))
2260 rc = vmdkDescDDBSetStr(pImage, pDescriptor, "ddb.adapterType", "ide");
2261
2262 return rc;
2263}
2264
2265static int vmdkParseDescriptor(PVMDKIMAGE pImage, char *pDescData, size_t cbDescData)
2266{
2267 int rc;
2268 unsigned cExtents;
2269 unsigned uLine;
2270 unsigned i;
2271
2272 rc = vmdkPreprocessDescriptor(pImage, pDescData, cbDescData,
2273 &pImage->Descriptor);
2274 if (RT_FAILURE(rc))
2275 return rc;
2276
2277 /* Check version, must be 1. */
2278 uint32_t uVersion;
2279 rc = vmdkDescBaseGetU32(&pImage->Descriptor, "version", &uVersion);
2280 if (RT_FAILURE(rc))
2281 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error finding key 'version' in descriptor in '%s'"), pImage->pszFilename);
2282 if (uVersion != 1)
2283 return vdIfError(pImage->pIfError, VERR_VD_VMDK_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VMDK: unsupported format version in descriptor in '%s'"), pImage->pszFilename);
2284
2285 /* Get image creation type and determine image flags. */
2286 char *pszCreateType = NULL; /* initialized to make gcc shut up */
2287 rc = vmdkDescBaseGetStr(pImage, &pImage->Descriptor, "createType",
2288 &pszCreateType);
2289 if (RT_FAILURE(rc))
2290 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot get image type from descriptor in '%s'"), pImage->pszFilename);
2291 if ( !strcmp(pszCreateType, "twoGbMaxExtentSparse")
2292 || !strcmp(pszCreateType, "twoGbMaxExtentFlat"))
2293 pImage->uImageFlags |= VD_VMDK_IMAGE_FLAGS_SPLIT_2G;
2294 else if ( !strcmp(pszCreateType, "partitionedDevice")
2295 || !strcmp(pszCreateType, "fullDevice"))
2296 pImage->uImageFlags |= VD_VMDK_IMAGE_FLAGS_RAWDISK;
2297 else if (!strcmp(pszCreateType, "streamOptimized"))
2298 pImage->uImageFlags |= VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED;
2299 else if (!strcmp(pszCreateType, "vmfs"))
2300 pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED | VD_VMDK_IMAGE_FLAGS_ESX;
2301 RTMemTmpFree(pszCreateType);
2302
2303 /* Count the number of extent config entries. */
2304 for (uLine = pImage->Descriptor.uFirstExtent, cExtents = 0;
2305 uLine != 0;
2306 uLine = pImage->Descriptor.aNextLines[uLine], cExtents++)
2307 /* nothing */;
2308
2309 if (!pImage->pDescData && cExtents != 1)
2310 {
2311 /* Monolithic image, must have only one extent (already opened). */
2312 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: monolithic image may only have one extent in '%s'"), pImage->pszFilename);
2313 }
2314
2315 if (pImage->pDescData)
2316 {
2317 /* Non-monolithic image, extents need to be allocated. */
2318 rc = vmdkCreateExtents(pImage, cExtents);
2319 if (RT_FAILURE(rc))
2320 return rc;
2321 }
2322
2323 for (i = 0, uLine = pImage->Descriptor.uFirstExtent;
2324 i < cExtents; i++, uLine = pImage->Descriptor.aNextLines[uLine])
2325 {
2326 char *pszLine = pImage->Descriptor.aLines[uLine];
2327
2328 /* Access type of the extent. */
2329 if (!strncmp(pszLine, "RW", 2))
2330 {
2331 pImage->pExtents[i].enmAccess = VMDKACCESS_READWRITE;
2332 pszLine += 2;
2333 }
2334 else if (!strncmp(pszLine, "RDONLY", 6))
2335 {
2336 pImage->pExtents[i].enmAccess = VMDKACCESS_READONLY;
2337 pszLine += 6;
2338 }
2339 else if (!strncmp(pszLine, "NOACCESS", 8))
2340 {
2341 pImage->pExtents[i].enmAccess = VMDKACCESS_NOACCESS;
2342 pszLine += 8;
2343 }
2344 else
2345 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2346 if (*pszLine++ != ' ')
2347 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2348
2349 /* Nominal size of the extent. */
2350 rc = RTStrToUInt64Ex(pszLine, &pszLine, 10,
2351 &pImage->pExtents[i].cNominalSectors);
2352 if (RT_FAILURE(rc))
2353 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2354 if (*pszLine++ != ' ')
2355 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2356
2357 /* Type of the extent. */
2358 if (!strncmp(pszLine, "SPARSE", 6))
2359 {
2360 pImage->pExtents[i].enmType = VMDKETYPE_HOSTED_SPARSE;
2361 pszLine += 6;
2362 }
2363 else if (!strncmp(pszLine, "FLAT", 4))
2364 {
2365 pImage->pExtents[i].enmType = VMDKETYPE_FLAT;
2366 pszLine += 4;
2367 }
2368 else if (!strncmp(pszLine, "ZERO", 4))
2369 {
2370 pImage->pExtents[i].enmType = VMDKETYPE_ZERO;
2371 pszLine += 4;
2372 }
2373 else if (!strncmp(pszLine, "VMFS", 4))
2374 {
2375 pImage->pExtents[i].enmType = VMDKETYPE_VMFS;
2376 pszLine += 4;
2377 }
2378 else
2379 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2380
2381 if (pImage->pExtents[i].enmType == VMDKETYPE_ZERO)
2382 {
2383 /* This one has no basename or offset. */
2384 if (*pszLine == ' ')
2385 pszLine++;
2386 if (*pszLine != '\0')
2387 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2388 pImage->pExtents[i].pszBasename = NULL;
2389 }
2390 else
2391 {
2392 /* All other extent types have basename and optional offset. */
2393 if (*pszLine++ != ' ')
2394 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2395
2396 /* Basename of the image. Surrounded by quotes. */
2397 char *pszBasename;
2398 rc = vmdkStringUnquote(pImage, pszLine, &pszBasename, &pszLine);
2399 if (RT_FAILURE(rc))
2400 return rc;
2401 pImage->pExtents[i].pszBasename = pszBasename;
2402 if (*pszLine == ' ')
2403 {
2404 pszLine++;
2405 if (*pszLine != '\0')
2406 {
2407 /* Optional offset in extent specified. */
2408 rc = RTStrToUInt64Ex(pszLine, &pszLine, 10,
2409 &pImage->pExtents[i].uSectorOffset);
2410 if (RT_FAILURE(rc))
2411 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2412 }
2413 }
2414
2415 if (*pszLine != '\0')
2416 return vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: parse error in extent description in '%s'"), pImage->pszFilename);
2417 }
2418 }
2419
2420 /* Determine PCHS geometry (autogenerate if necessary). */
2421 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
2422 VMDK_DDB_GEO_PCHS_CYLINDERS,
2423 &pImage->PCHSGeometry.cCylinders);
2424 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2425 pImage->PCHSGeometry.cCylinders = 0;
2426 else if (RT_FAILURE(rc))
2427 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error getting PCHS geometry from extent description in '%s'"), pImage->pszFilename);
2428 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
2429 VMDK_DDB_GEO_PCHS_HEADS,
2430 &pImage->PCHSGeometry.cHeads);
2431 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2432 pImage->PCHSGeometry.cHeads = 0;
2433 else if (RT_FAILURE(rc))
2434 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error getting PCHS geometry from extent description in '%s'"), pImage->pszFilename);
2435 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
2436 VMDK_DDB_GEO_PCHS_SECTORS,
2437 &pImage->PCHSGeometry.cSectors);
2438 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2439 pImage->PCHSGeometry.cSectors = 0;
2440 else if (RT_FAILURE(rc))
2441 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error getting PCHS geometry from extent description in '%s'"), pImage->pszFilename);
2442 if ( pImage->PCHSGeometry.cCylinders == 0
2443 || pImage->PCHSGeometry.cHeads == 0
2444 || pImage->PCHSGeometry.cHeads > 16
2445 || pImage->PCHSGeometry.cSectors == 0
2446 || pImage->PCHSGeometry.cSectors > 63)
2447 {
2448 /* Mark PCHS geometry as not yet valid (can't do the calculation here
2449 * as the total image size isn't known yet). */
2450 pImage->PCHSGeometry.cCylinders = 0;
2451 pImage->PCHSGeometry.cHeads = 16;
2452 pImage->PCHSGeometry.cSectors = 63;
2453 }
2454
2455 /* Determine LCHS geometry (set to 0 if not specified). */
2456 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
2457 VMDK_DDB_GEO_LCHS_CYLINDERS,
2458 &pImage->LCHSGeometry.cCylinders);
2459 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2460 pImage->LCHSGeometry.cCylinders = 0;
2461 else if (RT_FAILURE(rc))
2462 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error getting LCHS geometry from extent description in '%s'"), pImage->pszFilename);
2463 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
2464 VMDK_DDB_GEO_LCHS_HEADS,
2465 &pImage->LCHSGeometry.cHeads);
2466 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2467 pImage->LCHSGeometry.cHeads = 0;
2468 else if (RT_FAILURE(rc))
2469 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error getting LCHS geometry from extent description in '%s'"), pImage->pszFilename);
2470 rc = vmdkDescDDBGetU32(pImage, &pImage->Descriptor,
2471 VMDK_DDB_GEO_LCHS_SECTORS,
2472 &pImage->LCHSGeometry.cSectors);
2473 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2474 pImage->LCHSGeometry.cSectors = 0;
2475 else if (RT_FAILURE(rc))
2476 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error getting LCHS geometry from extent description in '%s'"), pImage->pszFilename);
2477 if ( pImage->LCHSGeometry.cCylinders == 0
2478 || pImage->LCHSGeometry.cHeads == 0
2479 || pImage->LCHSGeometry.cSectors == 0)
2480 {
2481 pImage->LCHSGeometry.cCylinders = 0;
2482 pImage->LCHSGeometry.cHeads = 0;
2483 pImage->LCHSGeometry.cSectors = 0;
2484 }
2485
2486 /* Get image UUID. */
2487 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, VMDK_DDB_IMAGE_UUID,
2488 &pImage->ImageUuid);
2489 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2490 {
2491 /* Image without UUID. Probably created by VMware and not yet used
2492 * by VirtualBox. Can only be added for images opened in read/write
2493 * mode, so don't bother producing a sensible UUID otherwise. */
2494 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2495 RTUuidClear(&pImage->ImageUuid);
2496 else
2497 {
2498 rc = RTUuidCreate(&pImage->ImageUuid);
2499 if (RT_FAILURE(rc))
2500 return rc;
2501 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2502 VMDK_DDB_IMAGE_UUID, &pImage->ImageUuid);
2503 if (RT_FAILURE(rc))
2504 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename);
2505 }
2506 }
2507 else if (RT_FAILURE(rc))
2508 return rc;
2509
2510 /* Get image modification UUID. */
2511 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor,
2512 VMDK_DDB_MODIFICATION_UUID,
2513 &pImage->ModificationUuid);
2514 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2515 {
2516 /* Image without UUID. Probably created by VMware and not yet used
2517 * by VirtualBox. Can only be added for images opened in read/write
2518 * mode, so don't bother producing a sensible UUID otherwise. */
2519 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2520 RTUuidClear(&pImage->ModificationUuid);
2521 else
2522 {
2523 rc = RTUuidCreate(&pImage->ModificationUuid);
2524 if (RT_FAILURE(rc))
2525 return rc;
2526 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2527 VMDK_DDB_MODIFICATION_UUID,
2528 &pImage->ModificationUuid);
2529 if (RT_FAILURE(rc))
2530 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error storing image modification UUID in descriptor in '%s'"), pImage->pszFilename);
2531 }
2532 }
2533 else if (RT_FAILURE(rc))
2534 return rc;
2535
2536 /* Get UUID of parent image. */
2537 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor, VMDK_DDB_PARENT_UUID,
2538 &pImage->ParentUuid);
2539 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2540 {
2541 /* Image without UUID. Probably created by VMware and not yet used
2542 * by VirtualBox. Can only be added for images opened in read/write
2543 * mode, so don't bother producing a sensible UUID otherwise. */
2544 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2545 RTUuidClear(&pImage->ParentUuid);
2546 else
2547 {
2548 rc = RTUuidClear(&pImage->ParentUuid);
2549 if (RT_FAILURE(rc))
2550 return rc;
2551 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2552 VMDK_DDB_PARENT_UUID, &pImage->ParentUuid);
2553 if (RT_FAILURE(rc))
2554 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error storing parent UUID in descriptor in '%s'"), pImage->pszFilename);
2555 }
2556 }
2557 else if (RT_FAILURE(rc))
2558 return rc;
2559
2560 /* Get parent image modification UUID. */
2561 rc = vmdkDescDDBGetUuid(pImage, &pImage->Descriptor,
2562 VMDK_DDB_PARENT_MODIFICATION_UUID,
2563 &pImage->ParentModificationUuid);
2564 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
2565 {
2566 /* Image without UUID. Probably created by VMware and not yet used
2567 * by VirtualBox. Can only be added for images opened in read/write
2568 * mode, so don't bother producing a sensible UUID otherwise. */
2569 if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2570 RTUuidClear(&pImage->ParentModificationUuid);
2571 else
2572 {
2573 RTUuidClear(&pImage->ParentModificationUuid);
2574 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
2575 VMDK_DDB_PARENT_MODIFICATION_UUID,
2576 &pImage->ParentModificationUuid);
2577 if (RT_FAILURE(rc))
2578 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error storing parent modification UUID in descriptor in '%s'"), pImage->pszFilename);
2579 }
2580 }
2581 else if (RT_FAILURE(rc))
2582 return rc;
2583
2584 return VINF_SUCCESS;
2585}
2586
2587/**
2588 * Internal : Prepares the descriptor to write to the image.
2589 */
2590static int vmdkDescriptorPrepare(PVMDKIMAGE pImage, uint64_t cbLimit,
2591 void **ppvData, size_t *pcbData)
2592{
2593 int rc = VINF_SUCCESS;
2594
2595 /*
2596 * Allocate temporary descriptor buffer.
2597 * In case there is no limit allocate a default
2598 * and increase if required.
2599 */
2600 size_t cbDescriptor = cbLimit ? cbLimit : 4 * _1K;
2601 char *pszDescriptor = (char *)RTMemAllocZ(cbDescriptor);
2602 size_t offDescriptor = 0;
2603
2604 if (!pszDescriptor)
2605 return VERR_NO_MEMORY;
2606
2607 for (unsigned i = 0; i < pImage->Descriptor.cLines; i++)
2608 {
2609 const char *psz = pImage->Descriptor.aLines[i];
2610 size_t cb = strlen(psz);
2611
2612 /*
2613 * Increase the descriptor if there is no limit and
2614 * there is not enough room left for this line.
2615 */
2616 if (offDescriptor + cb + 1 > cbDescriptor)
2617 {
2618 if (cbLimit)
2619 {
2620 rc = vdIfError(pImage->pIfError, VERR_BUFFER_OVERFLOW, RT_SRC_POS, N_("VMDK: descriptor too long in '%s'"), pImage->pszFilename);
2621 break;
2622 }
2623 else
2624 {
2625 char *pszDescriptorNew = NULL;
2626 LogFlow(("Increasing descriptor cache\n"));
2627
2628 pszDescriptorNew = (char *)RTMemRealloc(pszDescriptor, cbDescriptor + cb + 4 * _1K);
2629 if (!pszDescriptorNew)
2630 {
2631 rc = VERR_NO_MEMORY;
2632 break;
2633 }
2634 pszDescriptor = pszDescriptorNew;
2635 cbDescriptor += cb + 4 * _1K;
2636 }
2637 }
2638
2639 if (cb > 0)
2640 {
2641 memcpy(pszDescriptor + offDescriptor, psz, cb);
2642 offDescriptor += cb;
2643 }
2644
2645 memcpy(pszDescriptor + offDescriptor, "\n", 1);
2646 offDescriptor++;
2647 }
2648
2649 if (RT_SUCCESS(rc))
2650 {
2651 *ppvData = pszDescriptor;
2652 *pcbData = offDescriptor;
2653 }
2654 else if (pszDescriptor)
2655 RTMemFree(pszDescriptor);
2656
2657 return rc;
2658}
2659
2660/**
2661 * Internal: write/update the descriptor part of the image.
2662 */
2663static int vmdkWriteDescriptor(PVMDKIMAGE pImage, PVDIOCTX pIoCtx)
2664{
2665 int rc = VINF_SUCCESS;
2666 uint64_t cbLimit;
2667 uint64_t uOffset;
2668 PVMDKFILE pDescFile;
2669 void *pvDescriptor = NULL;
2670 size_t cbDescriptor;
2671
2672 if (pImage->pDescData)
2673 {
2674 /* Separate descriptor file. */
2675 uOffset = 0;
2676 cbLimit = 0;
2677 pDescFile = pImage->pFile;
2678 }
2679 else
2680 {
2681 /* Embedded descriptor file. */
2682 uOffset = VMDK_SECTOR2BYTE(pImage->pExtents[0].uDescriptorSector);
2683 cbLimit = VMDK_SECTOR2BYTE(pImage->pExtents[0].cDescriptorSectors);
2684 pDescFile = pImage->pExtents[0].pFile;
2685 }
2686 /* Bail out if there is no file to write to. */
2687 if (pDescFile == NULL)
2688 return VERR_INVALID_PARAMETER;
2689
2690 rc = vmdkDescriptorPrepare(pImage, cbLimit, &pvDescriptor, &cbDescriptor);
2691 if (RT_SUCCESS(rc))
2692 {
2693 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pDescFile->pStorage,
2694 uOffset, pvDescriptor,
2695 cbLimit ? cbLimit : cbDescriptor,
2696 pIoCtx, NULL, NULL);
2697 if ( RT_FAILURE(rc)
2698 && rc != VERR_VD_ASYNC_IO_IN_PROGRESS)
2699 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error writing descriptor in '%s'"), pImage->pszFilename);
2700 }
2701
2702 if (RT_SUCCESS(rc) && !cbLimit)
2703 {
2704 rc = vdIfIoIntFileSetSize(pImage->pIfIo, pDescFile->pStorage, cbDescriptor);
2705 if (RT_FAILURE(rc))
2706 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error truncating descriptor in '%s'"), pImage->pszFilename);
2707 }
2708
2709 if (RT_SUCCESS(rc))
2710 pImage->Descriptor.fDirty = false;
2711
2712 if (pvDescriptor)
2713 RTMemFree(pvDescriptor);
2714 return rc;
2715
2716}
2717
2718/**
2719 * Internal: validate the consistency check values in a binary header.
2720 */
2721static int vmdkValidateHeader(PVMDKIMAGE pImage, PVMDKEXTENT pExtent, const SparseExtentHeader *pHeader)
2722{
2723 int rc = VINF_SUCCESS;
2724 if (RT_LE2H_U32(pHeader->magicNumber) != VMDK_SPARSE_MAGICNUMBER)
2725 {
2726 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: incorrect magic in sparse extent header in '%s'"), pExtent->pszFullname);
2727 return rc;
2728 }
2729 if (RT_LE2H_U32(pHeader->version) != 1 && RT_LE2H_U32(pHeader->version) != 3)
2730 {
2731 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_UNSUPPORTED_VERSION, RT_SRC_POS, N_("VMDK: incorrect version in sparse extent header in '%s', not a VMDK 1.0/1.1 conforming file"), pExtent->pszFullname);
2732 return rc;
2733 }
2734 if ( (RT_LE2H_U32(pHeader->flags) & 1)
2735 && ( pHeader->singleEndLineChar != '\n'
2736 || pHeader->nonEndLineChar != ' '
2737 || pHeader->doubleEndLineChar1 != '\r'
2738 || pHeader->doubleEndLineChar2 != '\n') )
2739 {
2740 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: corrupted by CR/LF translation in '%s'"), pExtent->pszFullname);
2741 return rc;
2742 }
2743 if (RT_LE2H_U64(pHeader->descriptorSize) > VMDK_SPARSE_DESCRIPTOR_SIZE_MAX)
2744 {
2745 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: descriptor size out of bounds (%llu vs %llu) '%s'"),
2746 pExtent->pszFullname, RT_LE2H_U64(pHeader->descriptorSize), VMDK_SPARSE_DESCRIPTOR_SIZE_MAX);
2747 return rc;
2748 }
2749 return rc;
2750}
2751
2752/**
2753 * Internal: read metadata belonging to an extent with binary header, i.e.
2754 * as found in monolithic files.
2755 */
2756static int vmdkReadBinaryMetaExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
2757 bool fMagicAlreadyRead)
2758{
2759 SparseExtentHeader Header;
2760 int rc;
2761
2762 if (!fMagicAlreadyRead)
2763 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage, 0,
2764 &Header, sizeof(Header));
2765 else
2766 {
2767 Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);
2768 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
2769 RT_UOFFSETOF(SparseExtentHeader, version),
2770 &Header.version,
2771 sizeof(Header)
2772 - RT_UOFFSETOF(SparseExtentHeader, version));
2773 }
2774
2775 if (RT_SUCCESS(rc))
2776 {
2777 rc = vmdkValidateHeader(pImage, pExtent, &Header);
2778 if (RT_SUCCESS(rc))
2779 {
2780 uint64_t cbFile = 0;
2781
2782 if ( (RT_LE2H_U32(Header.flags) & RT_BIT(17))
2783 && RT_LE2H_U64(Header.gdOffset) == VMDK_GD_AT_END)
2784 pExtent->fFooter = true;
2785
2786 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2787 || ( pExtent->fFooter
2788 && !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL)))
2789 {
2790 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pExtent->pFile->pStorage, &cbFile);
2791 if (RT_FAILURE(rc))
2792 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot get size of '%s'"), pExtent->pszFullname);
2793 }
2794
2795 if (RT_SUCCESS(rc))
2796 {
2797 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
2798 pExtent->uAppendPosition = RT_ALIGN_64(cbFile, 512);
2799
2800 if ( pExtent->fFooter
2801 && ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2802 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL)))
2803 {
2804 /* Read the footer, which comes before the end-of-stream marker. */
2805 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
2806 cbFile - 2*512, &Header,
2807 sizeof(Header));
2808 if (RT_FAILURE(rc))
2809 {
2810 vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error reading extent footer in '%s'"), pExtent->pszFullname);
2811 rc = VERR_VD_VMDK_INVALID_HEADER;
2812 }
2813
2814 if (RT_SUCCESS(rc))
2815 rc = vmdkValidateHeader(pImage, pExtent, &Header);
2816 /* Prohibit any writes to this extent. */
2817 pExtent->uAppendPosition = 0;
2818 }
2819
2820 if (RT_SUCCESS(rc))
2821 {
2822 pExtent->uVersion = RT_LE2H_U32(Header.version);
2823 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE; /* Just dummy value, changed later. */
2824 pExtent->cSectors = RT_LE2H_U64(Header.capacity);
2825 pExtent->cSectorsPerGrain = RT_LE2H_U64(Header.grainSize);
2826 pExtent->uDescriptorSector = RT_LE2H_U64(Header.descriptorOffset);
2827 pExtent->cDescriptorSectors = RT_LE2H_U64(Header.descriptorSize);
2828 pExtent->cGTEntries = RT_LE2H_U32(Header.numGTEsPerGT);
2829 pExtent->cOverheadSectors = RT_LE2H_U64(Header.overHead);
2830 pExtent->fUncleanShutdown = !!Header.uncleanShutdown;
2831 pExtent->uCompression = RT_LE2H_U16(Header.compressAlgorithm);
2832 if (RT_LE2H_U32(Header.flags) & RT_BIT(1))
2833 {
2834 pExtent->uSectorRGD = RT_LE2H_U64(Header.rgdOffset);
2835 pExtent->uSectorGD = RT_LE2H_U64(Header.gdOffset);
2836 }
2837 else
2838 {
2839 pExtent->uSectorGD = RT_LE2H_U64(Header.gdOffset);
2840 pExtent->uSectorRGD = 0;
2841 }
2842
2843 if (pExtent->uDescriptorSector && !pExtent->cDescriptorSectors)
2844 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2845 N_("VMDK: inconsistent embedded descriptor config in '%s'"), pExtent->pszFullname);
2846
2847 if ( RT_SUCCESS(rc)
2848 && ( pExtent->uSectorGD == VMDK_GD_AT_END
2849 || pExtent->uSectorRGD == VMDK_GD_AT_END)
2850 && ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2851 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL)))
2852 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2853 N_("VMDK: cannot resolve grain directory offset in '%s'"), pExtent->pszFullname);
2854
2855 if (RT_SUCCESS(rc))
2856 {
2857 uint64_t cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
2858 if (!cSectorsPerGDE || cSectorsPerGDE > UINT32_MAX)
2859 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2860 N_("VMDK: incorrect grain directory size in '%s'"), pExtent->pszFullname);
2861 else
2862 {
2863 pExtent->cSectorsPerGDE = cSectorsPerGDE;
2864 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
2865
2866 /* Fix up the number of descriptor sectors, as some flat images have
2867 * really just one, and this causes failures when inserting the UUID
2868 * values and other extra information. */
2869 if (pExtent->cDescriptorSectors != 0 && pExtent->cDescriptorSectors < 4)
2870 {
2871 /* Do it the easy way - just fix it for flat images which have no
2872 * other complicated metadata which needs space too. */
2873 if ( pExtent->uDescriptorSector + 4 < pExtent->cOverheadSectors
2874 && pExtent->cGTEntries * pExtent->cGDEntries == 0)
2875 pExtent->cDescriptorSectors = 4;
2876 }
2877 }
2878 }
2879 }
2880 }
2881 }
2882 }
2883 else
2884 {
2885 vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error reading extent header in '%s'"), pExtent->pszFullname);
2886 rc = VERR_VD_VMDK_INVALID_HEADER;
2887 }
2888
2889 if (RT_FAILURE(rc))
2890 vmdkFreeExtentData(pImage, pExtent, false);
2891
2892 return rc;
2893}
2894
2895/**
2896 * Internal: read additional metadata belonging to an extent. For those
2897 * extents which have no additional metadata just verify the information.
2898 */
2899static int vmdkReadMetaExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
2900{
2901 int rc = VINF_SUCCESS;
2902
2903/* disabled the check as there are too many truncated vmdk images out there */
2904#ifdef VBOX_WITH_VMDK_STRICT_SIZE_CHECK
2905 uint64_t cbExtentSize;
2906 /* The image must be a multiple of a sector in size and contain the data
2907 * area (flat images only). If not, it means the image is at least
2908 * truncated, or even seriously garbled. */
2909 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pExtent->pFile->pStorage, &cbExtentSize);
2910 if (RT_FAILURE(rc))
2911 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error getting size in '%s'"), pExtent->pszFullname);
2912 else if ( cbExtentSize != RT_ALIGN_64(cbExtentSize, 512)
2913 && (pExtent->enmType != VMDKETYPE_FLAT || pExtent->cNominalSectors + pExtent->uSectorOffset > VMDK_BYTE2SECTOR(cbExtentSize)))
2914 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2915 N_("VMDK: file size is not a multiple of 512 in '%s', file is truncated or otherwise garbled"), pExtent->pszFullname);
2916#endif /* VBOX_WITH_VMDK_STRICT_SIZE_CHECK */
2917 if ( RT_SUCCESS(rc)
2918 && pExtent->enmType == VMDKETYPE_HOSTED_SPARSE)
2919 {
2920 /* The spec says that this must be a power of two and greater than 8,
2921 * but probably they meant not less than 8. */
2922 if ( (pExtent->cSectorsPerGrain & (pExtent->cSectorsPerGrain - 1))
2923 || pExtent->cSectorsPerGrain < 8)
2924 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2925 N_("VMDK: invalid extent grain size %u in '%s'"), pExtent->cSectorsPerGrain, pExtent->pszFullname);
2926 else
2927 {
2928 /* This code requires that a grain table must hold a power of two multiple
2929 * of the number of entries per GT cache entry. */
2930 if ( (pExtent->cGTEntries & (pExtent->cGTEntries - 1))
2931 || pExtent->cGTEntries < VMDK_GT_CACHELINE_SIZE)
2932 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
2933 N_("VMDK: grain table cache size problem in '%s'"), pExtent->pszFullname);
2934 else
2935 {
2936 rc = vmdkAllocStreamBuffers(pImage, pExtent);
2937 if (RT_SUCCESS(rc))
2938 {
2939 /* Prohibit any writes to this streamOptimized extent. */
2940 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
2941 pExtent->uAppendPosition = 0;
2942
2943 if ( !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
2944 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
2945 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
2946 rc = vmdkReadGrainDirectory(pImage, pExtent);
2947 else
2948 {
2949 pExtent->uGrainSectorAbs = pExtent->cOverheadSectors;
2950 pExtent->cbGrainStreamRead = 0;
2951 }
2952 }
2953 }
2954 }
2955 }
2956
2957 if (RT_FAILURE(rc))
2958 vmdkFreeExtentData(pImage, pExtent, false);
2959
2960 return rc;
2961}
2962
2963/**
2964 * Internal: write/update the metadata for a sparse extent.
2965 */
2966static int vmdkWriteMetaSparseExtent(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
2967 uint64_t uOffset, PVDIOCTX pIoCtx)
2968{
2969 SparseExtentHeader Header;
2970
2971 memset(&Header, '\0', sizeof(Header));
2972 Header.magicNumber = RT_H2LE_U32(VMDK_SPARSE_MAGICNUMBER);
2973 Header.version = RT_H2LE_U32(pExtent->uVersion);
2974 Header.flags = RT_H2LE_U32(RT_BIT(0));
2975 if (pExtent->pRGD)
2976 Header.flags |= RT_H2LE_U32(RT_BIT(1));
2977 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
2978 Header.flags |= RT_H2LE_U32(RT_BIT(16) | RT_BIT(17));
2979 Header.capacity = RT_H2LE_U64(pExtent->cSectors);
2980 Header.grainSize = RT_H2LE_U64(pExtent->cSectorsPerGrain);
2981 Header.descriptorOffset = RT_H2LE_U64(pExtent->uDescriptorSector);
2982 Header.descriptorSize = RT_H2LE_U64(pExtent->cDescriptorSectors);
2983 Header.numGTEsPerGT = RT_H2LE_U32(pExtent->cGTEntries);
2984 if (pExtent->fFooter && uOffset == 0)
2985 {
2986 if (pExtent->pRGD)
2987 {
2988 Assert(pExtent->uSectorRGD);
2989 Header.rgdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
2990 Header.gdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
2991 }
2992 else
2993 Header.gdOffset = RT_H2LE_U64(VMDK_GD_AT_END);
2994 }
2995 else
2996 {
2997 if (pExtent->pRGD)
2998 {
2999 Assert(pExtent->uSectorRGD);
3000 Header.rgdOffset = RT_H2LE_U64(pExtent->uSectorRGD);
3001 Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
3002 }
3003 else
3004 Header.gdOffset = RT_H2LE_U64(pExtent->uSectorGD);
3005 }
3006 Header.overHead = RT_H2LE_U64(pExtent->cOverheadSectors);
3007 Header.uncleanShutdown = pExtent->fUncleanShutdown;
3008 Header.singleEndLineChar = '\n';
3009 Header.nonEndLineChar = ' ';
3010 Header.doubleEndLineChar1 = '\r';
3011 Header.doubleEndLineChar2 = '\n';
3012 Header.compressAlgorithm = RT_H2LE_U16(pExtent->uCompression);
3013
3014 int rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
3015 uOffset, &Header, sizeof(Header),
3016 pIoCtx, NULL, NULL);
3017 if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
3018 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error writing extent header in '%s'"), pExtent->pszFullname);
3019 return rc;
3020}
3021
3022/**
3023 * Internal: free the buffers used for streamOptimized images.
3024 */
3025static void vmdkFreeStreamBuffers(PVMDKEXTENT pExtent)
3026{
3027 if (pExtent->pvCompGrain)
3028 {
3029 RTMemFree(pExtent->pvCompGrain);
3030 pExtent->pvCompGrain = NULL;
3031 }
3032 if (pExtent->pvGrain)
3033 {
3034 RTMemFree(pExtent->pvGrain);
3035 pExtent->pvGrain = NULL;
3036 }
3037}
3038
3039/**
3040 * Internal: free the memory used by the extent data structure, optionally
3041 * deleting the referenced files.
3042 *
3043 * @returns VBox status code.
3044 * @param pImage Pointer to the image instance data.
3045 * @param pExtent The extent to free.
3046 * @param fDelete Flag whether to delete the backing storage.
3047 */
3048static int vmdkFreeExtentData(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
3049 bool fDelete)
3050{
3051 int rc = VINF_SUCCESS;
3052
3053 vmdkFreeGrainDirectory(pExtent);
3054 if (pExtent->pDescData)
3055 {
3056 RTMemFree(pExtent->pDescData);
3057 pExtent->pDescData = NULL;
3058 }
3059 if (pExtent->pFile != NULL)
3060 {
3061 /* Do not delete raw extents, these have full and base names equal. */
3062 rc = vmdkFileClose(pImage, &pExtent->pFile,
3063 fDelete
3064 && pExtent->pszFullname
3065 && pExtent->pszBasename
3066 && strcmp(pExtent->pszFullname, pExtent->pszBasename));
3067 }
3068 if (pExtent->pszBasename)
3069 {
3070 RTMemTmpFree((void *)pExtent->pszBasename);
3071 pExtent->pszBasename = NULL;
3072 }
3073 if (pExtent->pszFullname)
3074 {
3075 RTStrFree((char *)(void *)pExtent->pszFullname);
3076 pExtent->pszFullname = NULL;
3077 }
3078 vmdkFreeStreamBuffers(pExtent);
3079
3080 return rc;
3081}
3082
3083/**
3084 * Internal: allocate grain table cache if necessary for this image.
3085 */
3086static int vmdkAllocateGrainTableCache(PVMDKIMAGE pImage)
3087{
3088 PVMDKEXTENT pExtent;
3089
3090 /* Allocate grain table cache if any sparse extent is present. */
3091 for (unsigned i = 0; i < pImage->cExtents; i++)
3092 {
3093 pExtent = &pImage->pExtents[i];
3094 if (pExtent->enmType == VMDKETYPE_HOSTED_SPARSE)
3095 {
3096 /* Allocate grain table cache. */
3097 pImage->pGTCache = (PVMDKGTCACHE)RTMemAllocZ(sizeof(VMDKGTCACHE));
3098 if (!pImage->pGTCache)
3099 return VERR_NO_MEMORY;
3100 for (unsigned j = 0; j < VMDK_GT_CACHE_SIZE; j++)
3101 {
3102 PVMDKGTCACHEENTRY pGCE = &pImage->pGTCache->aGTCache[j];
3103 pGCE->uExtent = UINT32_MAX;
3104 }
3105 pImage->pGTCache->cEntries = VMDK_GT_CACHE_SIZE;
3106 break;
3107 }
3108 }
3109
3110 return VINF_SUCCESS;
3111}
3112
3113/**
3114 * Internal: allocate the given number of extents.
3115 */
3116static int vmdkCreateExtents(PVMDKIMAGE pImage, unsigned cExtents)
3117{
3118 int rc = VINF_SUCCESS;
3119 PVMDKEXTENT pExtents = (PVMDKEXTENT)RTMemAllocZ(cExtents * sizeof(VMDKEXTENT));
3120 if (pExtents)
3121 {
3122 for (unsigned i = 0; i < cExtents; i++)
3123 {
3124 pExtents[i].pFile = NULL;
3125 pExtents[i].pszBasename = NULL;
3126 pExtents[i].pszFullname = NULL;
3127 pExtents[i].pGD = NULL;
3128 pExtents[i].pRGD = NULL;
3129 pExtents[i].pDescData = NULL;
3130 pExtents[i].uVersion = 1;
3131 pExtents[i].uCompression = VMDK_COMPRESSION_NONE;
3132 pExtents[i].uExtent = i;
3133 pExtents[i].pImage = pImage;
3134 }
3135 pImage->pExtents = pExtents;
3136 pImage->cExtents = cExtents;
3137 }
3138 else
3139 rc = VERR_NO_MEMORY;
3140
3141 return rc;
3142}
3143
3144/**
3145 * Internal: Create an additional file backed extent in split images.
3146 * Supports split sparse and flat images.
3147 *
3148 * @returns VBox status code.
3149 * @param pImage VMDK image instance.
3150 * @param cbSize Desiried size in bytes of new extent.
3151 */
3152static int vmdkAddFileBackedExtent(PVMDKIMAGE pImage, uint64_t cbSize)
3153{
3154 int rc = VINF_SUCCESS;
3155 unsigned uImageFlags = pImage->uImageFlags;
3156
3157 /* Check for unsupported image type. */
3158 if ((uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX)
3159 || (uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
3160 || (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK))
3161 {
3162 return VERR_NOT_SUPPORTED;
3163 }
3164
3165 /* Allocate array of extents and copy existing extents to it. */
3166 PVMDKEXTENT pNewExtents = (PVMDKEXTENT)RTMemAllocZ((pImage->cExtents + 1) * sizeof(VMDKEXTENT));
3167 if (!pNewExtents)
3168 {
3169 return VERR_NO_MEMORY;
3170 }
3171
3172 memcpy(pNewExtents, pImage->pExtents, pImage->cExtents * sizeof(VMDKEXTENT));
3173
3174 /* Locate newly created extent and populate default metadata. */
3175 PVMDKEXTENT pExtent = &pNewExtents[pImage->cExtents];
3176
3177 pExtent->pFile = NULL;
3178 pExtent->pszBasename = NULL;
3179 pExtent->pszFullname = NULL;
3180 pExtent->pGD = NULL;
3181 pExtent->pRGD = NULL;
3182 pExtent->pDescData = NULL;
3183 pExtent->uVersion = 1;
3184 pExtent->uCompression = VMDK_COMPRESSION_NONE;
3185 pExtent->uExtent = pImage->cExtents;
3186 pExtent->pImage = pImage;
3187 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize);
3188 pExtent->enmAccess = VMDKACCESS_READWRITE;
3189 pExtent->uSectorOffset = 0;
3190 pExtent->fMetaDirty = true;
3191
3192 /* Apply image type specific meta data. */
3193 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
3194 {
3195 pExtent->enmType = VMDKETYPE_FLAT;
3196 }
3197 else
3198 {
3199 uint64_t cSectorsPerGDE, cSectorsPerGD;
3200 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;
3201 pExtent->cSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(cbSize, _64K));
3202 pExtent->cSectorsPerGrain = VMDK_BYTE2SECTOR(_64K);
3203 pExtent->cGTEntries = 512;
3204 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
3205 pExtent->cSectorsPerGDE = cSectorsPerGDE;
3206 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
3207 cSectorsPerGD = (pExtent->cGDEntries + (512 / sizeof(uint32_t) - 1)) / (512 / sizeof(uint32_t));
3208 }
3209
3210 /* Allocate and set file name for extent. */
3211 char *pszBasenameSubstr = RTPathFilename(pImage->pszFilename);
3212 AssertPtr(pszBasenameSubstr);
3213
3214 char *pszBasenameSuff = RTPathSuffix(pszBasenameSubstr);
3215 char *pszBasenameBase = RTStrDup(pszBasenameSubstr);
3216 RTPathStripSuffix(pszBasenameBase);
3217 char *pszTmp;
3218 size_t cbTmp;
3219
3220 if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED)
3221 RTStrAPrintf(&pszTmp, "%s-f%03d%s", pszBasenameBase,
3222 pExtent->uExtent + 1, pszBasenameSuff);
3223 else
3224 RTStrAPrintf(&pszTmp, "%s-s%03d%s", pszBasenameBase, pExtent->uExtent + 1,
3225 pszBasenameSuff);
3226
3227 RTStrFree(pszBasenameBase);
3228 if (!pszTmp)
3229 return VERR_NO_STR_MEMORY;
3230 cbTmp = strlen(pszTmp) + 1;
3231 char *pszBasename = (char *)RTMemTmpAlloc(cbTmp);
3232 if (!pszBasename)
3233 {
3234 RTStrFree(pszTmp);
3235 return VERR_NO_MEMORY;
3236 }
3237
3238 memcpy(pszBasename, pszTmp, cbTmp);
3239 RTStrFree(pszTmp);
3240
3241 pExtent->pszBasename = pszBasename;
3242
3243 char *pszBasedirectory = RTStrDup(pImage->pszFilename);
3244 if (!pszBasedirectory)
3245 return VERR_NO_STR_MEMORY;
3246 RTPathStripFilename(pszBasedirectory);
3247 char *pszFullname = RTPathJoinA(pszBasedirectory, pExtent->pszBasename);
3248 RTStrFree(pszBasedirectory);
3249 if (!pszFullname)
3250 return VERR_NO_STR_MEMORY;
3251 pExtent->pszFullname = pszFullname;
3252
3253 /* Create file for extent. */
3254 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszBasename, pExtent->pszFullname,
3255 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
3256 true /* fCreate */));
3257 if (RT_FAILURE(rc))
3258 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname);
3259
3260 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
3261 {
3262 /* For flat images: Pre allocate file space. */
3263 rc = vdIfIoIntFileSetAllocationSize(pImage->pIfIo, pExtent->pFile->pStorage, cbSize,
3264 0 /* fFlags */, NULL, 0, 0);
3265 if (RT_FAILURE(rc))
3266 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set size of new file '%s'"), pExtent->pszFullname);
3267 }
3268 else
3269 {
3270 /* For sparse images: Allocate new grain directories/tables. */
3271 /* fPreAlloc should never be false because VMware can't use such images. */
3272 rc = vmdkCreateGrainDirectory(pImage, pExtent,
3273 RT_MAX( pExtent->uDescriptorSector
3274 + pExtent->cDescriptorSectors,
3275 1),
3276 true /* fPreAlloc */);
3277 if (RT_FAILURE(rc))
3278 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new grain directory in '%s'"), pExtent->pszFullname);
3279 }
3280
3281 /* Insert new extent into descriptor file. */
3282 rc = vmdkDescExtInsert(pImage, &pImage->Descriptor, pExtent->enmAccess,
3283 pExtent->cNominalSectors, pExtent->enmType,
3284 pExtent->pszBasename, pExtent->uSectorOffset);
3285 if (RT_FAILURE(rc))
3286 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not insert the extent list into descriptor in '%s'"), pImage->pszFilename);
3287
3288 pImage->pExtents = pNewExtents;
3289 pImage->cExtents++;
3290
3291 return rc;
3292}
3293
3294/**
3295 * Reads and processes the descriptor embedded in sparse images.
3296 *
3297 * @returns VBox status code.
3298 * @param pImage VMDK image instance.
3299 * @param pFile The sparse file handle.
3300 */
3301static int vmdkDescriptorReadSparse(PVMDKIMAGE pImage, PVMDKFILE pFile)
3302{
3303 /* It's a hosted single-extent image. */
3304 int rc = vmdkCreateExtents(pImage, 1);
3305 if (RT_SUCCESS(rc))
3306 {
3307 /* The opened file is passed to the extent. No separate descriptor
3308 * file, so no need to keep anything open for the image. */
3309 PVMDKEXTENT pExtent = &pImage->pExtents[0];
3310 pExtent->pFile = pFile;
3311 pImage->pFile = NULL;
3312 pExtent->pszFullname = RTPathAbsDup(pImage->pszFilename);
3313 if (RT_LIKELY(pExtent->pszFullname))
3314 {
3315 /* As we're dealing with a monolithic image here, there must
3316 * be a descriptor embedded in the image file. */
3317 rc = vmdkReadBinaryMetaExtent(pImage, pExtent, true /* fMagicAlreadyRead */);
3318 if ( RT_SUCCESS(rc)
3319 && pExtent->uDescriptorSector
3320 && pExtent->cDescriptorSectors)
3321 {
3322 /* HACK: extend the descriptor if it is unusually small and it fits in
3323 * the unused space after the image header. Allows opening VMDK files
3324 * with extremely small descriptor in read/write mode.
3325 *
3326 * The previous version introduced a possible regression for VMDK stream
3327 * optimized images from VMware which tend to have only a single sector sized
3328 * descriptor. Increasing the descriptor size resulted in adding the various uuid
3329 * entries required to make it work with VBox but for stream optimized images
3330 * the updated binary header wasn't written to the disk creating a mismatch
3331 * between advertised and real descriptor size.
3332 *
3333 * The descriptor size will be increased even if opened readonly now if there
3334 * enough room but the new value will not be written back to the image.
3335 */
3336 if ( pExtent->cDescriptorSectors < 3
3337 && (int64_t)pExtent->uSectorGD - pExtent->uDescriptorSector >= 4
3338 && (!pExtent->uSectorRGD || (int64_t)pExtent->uSectorRGD - pExtent->uDescriptorSector >= 4))
3339 {
3340 uint64_t cDescriptorSectorsOld = pExtent->cDescriptorSectors;
3341
3342 pExtent->cDescriptorSectors = 4;
3343 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3344 {
3345 /*
3346 * Update the on disk number now to make sure we don't introduce inconsistencies
3347 * in case of stream optimized images from VMware where the descriptor is just
3348 * one sector big (the binary header is not written to disk for complete
3349 * stream optimized images in vmdkFlushImage()).
3350 */
3351 uint64_t u64DescSizeNew = RT_H2LE_U64(pExtent->cDescriptorSectors);
3352 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pFile->pStorage,
3353 RT_UOFFSETOF(SparseExtentHeader, descriptorSize),
3354 &u64DescSizeNew, sizeof(u64DescSizeNew));
3355 if (RT_FAILURE(rc))
3356 {
3357 LogFlowFunc(("Increasing the descriptor size failed with %Rrc\n", rc));
3358 /* Restore the old size and carry on. */
3359 pExtent->cDescriptorSectors = cDescriptorSectorsOld;
3360 }
3361 }
3362 }
3363 /* Read the descriptor from the extent. */
3364 pExtent->pDescData = (char *)RTMemAllocZ(VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
3365 if (RT_LIKELY(pExtent->pDescData))
3366 {
3367 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
3368 VMDK_SECTOR2BYTE(pExtent->uDescriptorSector),
3369 pExtent->pDescData,
3370 VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
3371 if (RT_SUCCESS(rc))
3372 {
3373 rc = vmdkParseDescriptor(pImage, pExtent->pDescData,
3374 VMDK_SECTOR2BYTE(pExtent->cDescriptorSectors));
3375 if ( RT_SUCCESS(rc)
3376 && ( !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
3377 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_ASYNC_IO)))
3378 {
3379 rc = vmdkReadMetaExtent(pImage, pExtent);
3380 if (RT_SUCCESS(rc))
3381 {
3382 /* Mark the extent as unclean if opened in read-write mode. */
3383 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3384 && !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
3385 {
3386 pExtent->fUncleanShutdown = true;
3387 pExtent->fMetaDirty = true;
3388 }
3389 }
3390 }
3391 else if (RT_SUCCESS(rc))
3392 rc = VERR_NOT_SUPPORTED;
3393 }
3394 else
3395 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in '%s'"), pExtent->pszFullname);
3396 }
3397 else
3398 rc = VERR_NO_MEMORY;
3399 }
3400 else if (RT_SUCCESS(rc))
3401 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: monolithic image without descriptor in '%s'"), pImage->pszFilename);
3402 }
3403 else
3404 rc = VERR_NO_MEMORY;
3405 }
3406
3407 return rc;
3408}
3409
3410/**
3411 * Reads the descriptor from a pure text file.
3412 *
3413 * @returns VBox status code.
3414 * @param pImage VMDK image instance.
3415 * @param pFile The descriptor file handle.
3416 */
3417static int vmdkDescriptorReadAscii(PVMDKIMAGE pImage, PVMDKFILE pFile)
3418{
3419 /* Allocate at least 10K, and make sure that there is 5K free space
3420 * in case new entries need to be added to the descriptor. Never
3421 * allocate more than 128K, because that's no valid descriptor file
3422 * and will result in the correct "truncated read" error handling. */
3423 uint64_t cbFileSize;
3424 int rc = vdIfIoIntFileGetSize(pImage->pIfIo, pFile->pStorage, &cbFileSize);
3425 if ( RT_SUCCESS(rc)
3426 && cbFileSize >= 50)
3427 {
3428 uint64_t cbSize = cbFileSize;
3429 if (cbSize % VMDK_SECTOR2BYTE(10))
3430 cbSize += VMDK_SECTOR2BYTE(20) - cbSize % VMDK_SECTOR2BYTE(10);
3431 else
3432 cbSize += VMDK_SECTOR2BYTE(10);
3433 cbSize = RT_MIN(cbSize, _128K);
3434 pImage->cbDescAlloc = RT_MAX(VMDK_SECTOR2BYTE(20), cbSize);
3435 pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc);
3436 if (RT_LIKELY(pImage->pDescData))
3437 {
3438 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pFile->pStorage, 0, pImage->pDescData,
3439 RT_MIN(pImage->cbDescAlloc, cbFileSize));
3440 if (RT_SUCCESS(rc))
3441 {
3442#if 0 /** @todo Revisit */
3443 cbRead += sizeof(u32Magic);
3444 if (cbRead == pImage->cbDescAlloc)
3445 {
3446 /* Likely the read is truncated. Better fail a bit too early
3447 * (normally the descriptor is much smaller than our buffer). */
3448 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: cannot read descriptor in '%s'"), pImage->pszFilename);
3449 goto out;
3450 }
3451#endif
3452 rc = vmdkParseDescriptor(pImage, pImage->pDescData,
3453 pImage->cbDescAlloc);
3454 if (RT_SUCCESS(rc))
3455 {
3456 for (unsigned i = 0; i < pImage->cExtents && RT_SUCCESS(rc); i++)
3457 {
3458 PVMDKEXTENT pExtent = &pImage->pExtents[i];
3459 if (pExtent->pszBasename)
3460 {
3461 /* Hack to figure out whether the specified name in the
3462 * extent descriptor is absolute. Doesn't always work, but
3463 * should be good enough for now. */
3464 char *pszFullname;
3465 /** @todo implement proper path absolute check. */
3466 if (pExtent->pszBasename[0] == RTPATH_SLASH)
3467 {
3468 pszFullname = RTStrDup(pExtent->pszBasename);
3469 if (!pszFullname)
3470 {
3471 rc = VERR_NO_MEMORY;
3472 break;
3473 }
3474 }
3475 else
3476 {
3477 char *pszDirname = RTStrDup(pImage->pszFilename);
3478 if (!pszDirname)
3479 {
3480 rc = VERR_NO_MEMORY;
3481 break;
3482 }
3483 RTPathStripFilename(pszDirname);
3484 pszFullname = RTPathJoinA(pszDirname, pExtent->pszBasename);
3485 RTStrFree(pszDirname);
3486 if (!pszFullname)
3487 {
3488 rc = VERR_NO_STR_MEMORY;
3489 break;
3490 }
3491 }
3492 pExtent->pszFullname = pszFullname;
3493 }
3494 else
3495 pExtent->pszFullname = NULL;
3496
3497 unsigned uOpenFlags = pImage->uOpenFlags | ((pExtent->enmAccess == VMDKACCESS_READONLY) ? VD_OPEN_FLAGS_READONLY : 0);
3498 switch (pExtent->enmType)
3499 {
3500 case VMDKETYPE_HOSTED_SPARSE:
3501 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszBasename, pExtent->pszFullname,
3502 VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */));
3503 if (RT_FAILURE(rc))
3504 {
3505 /* Do NOT signal an appropriate error here, as the VD
3506 * layer has the choice of retrying the open if it
3507 * failed. */
3508 break;
3509 }
3510 rc = vmdkReadBinaryMetaExtent(pImage, pExtent,
3511 false /* fMagicAlreadyRead */);
3512 if (RT_FAILURE(rc))
3513 break;
3514 rc = vmdkReadMetaExtent(pImage, pExtent);
3515 if (RT_FAILURE(rc))
3516 break;
3517
3518 /* Mark extent as unclean if opened in read-write mode. */
3519 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
3520 {
3521 pExtent->fUncleanShutdown = true;
3522 pExtent->fMetaDirty = true;
3523 }
3524 break;
3525 case VMDKETYPE_VMFS:
3526 case VMDKETYPE_FLAT:
3527 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszBasename, pExtent->pszFullname,
3528 VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */));
3529 if (RT_FAILURE(rc))
3530 {
3531 /* Do NOT signal an appropriate error here, as the VD
3532 * layer has the choice of retrying the open if it
3533 * failed. */
3534 break;
3535 }
3536 break;
3537 case VMDKETYPE_ZERO:
3538 /* Nothing to do. */
3539 break;
3540 default:
3541 AssertMsgFailed(("unknown vmdk extent type %d\n", pExtent->enmType));
3542 }
3543 }
3544 }
3545 }
3546 else
3547 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: read error for descriptor in '%s'"), pImage->pszFilename);
3548 }
3549 else
3550 rc = VERR_NO_MEMORY;
3551 }
3552 else if (RT_SUCCESS(rc))
3553 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS, N_("VMDK: descriptor in '%s' is too short"), pImage->pszFilename);
3554
3555 return rc;
3556}
3557
3558/**
3559 * Read and process the descriptor based on the image type.
3560 *
3561 * @returns VBox status code.
3562 * @param pImage VMDK image instance.
3563 * @param pFile VMDK file handle.
3564 */
3565static int vmdkDescriptorRead(PVMDKIMAGE pImage, PVMDKFILE pFile)
3566{
3567 uint32_t u32Magic;
3568
3569 /* Read magic (if present). */
3570 int rc = vdIfIoIntFileReadSync(pImage->pIfIo, pFile->pStorage, 0,
3571 &u32Magic, sizeof(u32Magic));
3572 if (RT_SUCCESS(rc))
3573 {
3574 /* Handle the file according to its magic number. */
3575 if (RT_LE2H_U32(u32Magic) == VMDK_SPARSE_MAGICNUMBER)
3576 rc = vmdkDescriptorReadSparse(pImage, pFile);
3577 else
3578 rc = vmdkDescriptorReadAscii(pImage, pFile);
3579 }
3580 else
3581 {
3582 vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error reading the magic number in '%s'"), pImage->pszFilename);
3583 rc = VERR_VD_VMDK_INVALID_HEADER;
3584 }
3585
3586 return rc;
3587}
3588
3589/**
3590 * Internal: Open an image, constructing all necessary data structures.
3591 */
3592static int vmdkOpenImage(PVMDKIMAGE pImage, unsigned uOpenFlags)
3593{
3594 pImage->uOpenFlags = uOpenFlags;
3595 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
3596 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
3597 AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
3598
3599 /*
3600 * Open the image.
3601 * We don't have to check for asynchronous access because
3602 * we only support raw access and the opened file is a description
3603 * file were no data is stored.
3604 */
3605 PVMDKFILE pFile;
3606 int rc = vmdkFileOpen(pImage, &pFile, NULL, pImage->pszFilename,
3607 VDOpenFlagsToFileOpenFlags(uOpenFlags, false /* fCreate */));
3608 if (RT_SUCCESS(rc))
3609 {
3610 pImage->pFile = pFile;
3611
3612 rc = vmdkDescriptorRead(pImage, pFile);
3613 if (RT_SUCCESS(rc))
3614 {
3615 /* Determine PCHS geometry if not set. */
3616 if (pImage->PCHSGeometry.cCylinders == 0)
3617 {
3618 uint64_t cCylinders = VMDK_BYTE2SECTOR(pImage->cbSize)
3619 / pImage->PCHSGeometry.cHeads
3620 / pImage->PCHSGeometry.cSectors;
3621 pImage->PCHSGeometry.cCylinders = (unsigned)RT_MIN(cCylinders, 16383);
3622 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3623 && !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
3624 {
3625 rc = vmdkDescSetPCHSGeometry(pImage, &pImage->PCHSGeometry);
3626 AssertRC(rc);
3627 }
3628 }
3629
3630 /* Update the image metadata now in case has changed. */
3631 rc = vmdkFlushImage(pImage, NULL);
3632 if (RT_SUCCESS(rc))
3633 {
3634 /* Figure out a few per-image constants from the extents. */
3635 pImage->cbSize = 0;
3636 for (unsigned i = 0; i < pImage->cExtents; i++)
3637 {
3638 PVMDKEXTENT pExtent = &pImage->pExtents[i];
3639 if (pExtent->enmType == VMDKETYPE_HOSTED_SPARSE)
3640 {
3641 /* Here used to be a check whether the nominal size of an extent
3642 * is a multiple of the grain size. The spec says that this is
3643 * always the case, but unfortunately some files out there in the
3644 * wild violate the spec (e.g. ReactOS 0.3.1). */
3645 }
3646 else if ( pExtent->enmType == VMDKETYPE_FLAT
3647 || pExtent->enmType == VMDKETYPE_ZERO)
3648 pImage->uImageFlags |= VD_IMAGE_FLAGS_FIXED;
3649
3650 pImage->cbSize += VMDK_SECTOR2BYTE(pExtent->cNominalSectors);
3651 }
3652
3653 if ( !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
3654 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
3655 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
3656 rc = vmdkAllocateGrainTableCache(pImage);
3657 }
3658 }
3659 }
3660 /* else: Do NOT signal an appropriate error here, as the VD layer has the
3661 * choice of retrying the open if it failed. */
3662
3663 if (RT_SUCCESS(rc))
3664 {
3665 PVDREGIONDESC pRegion = &pImage->RegionList.aRegions[0];
3666 pImage->RegionList.fFlags = 0;
3667 pImage->RegionList.cRegions = 1;
3668
3669 pRegion->offRegion = 0; /* Disk start. */
3670 pRegion->cbBlock = 512;
3671 pRegion->enmDataForm = VDREGIONDATAFORM_RAW;
3672 pRegion->enmMetadataForm = VDREGIONMETADATAFORM_NONE;
3673 pRegion->cbData = 512;
3674 pRegion->cbMetadata = 0;
3675 pRegion->cRegionBlocksOrBytes = pImage->cbSize;
3676 }
3677 else
3678 vmdkFreeImage(pImage, false, false /*fFlush*/); /* Don't try to flush anything if opening failed. */
3679 return rc;
3680}
3681
3682/**
3683 * Frees a raw descriptor.
3684 * @internal
3685 */
3686static int vmdkRawDescFree(PVDISKRAW pRawDesc)
3687{
3688 if (!pRawDesc)
3689 return VINF_SUCCESS;
3690
3691 RTStrFree(pRawDesc->pszRawDisk);
3692 pRawDesc->pszRawDisk = NULL;
3693
3694 /* Partitions: */
3695 for (unsigned i = 0; i < pRawDesc->cPartDescs; i++)
3696 {
3697 RTStrFree(pRawDesc->pPartDescs[i].pszRawDevice);
3698 pRawDesc->pPartDescs[i].pszRawDevice = NULL;
3699
3700 RTMemFree(pRawDesc->pPartDescs[i].pvPartitionData);
3701 pRawDesc->pPartDescs[i].pvPartitionData = NULL;
3702 }
3703
3704 RTMemFree(pRawDesc->pPartDescs);
3705 pRawDesc->pPartDescs = NULL;
3706
3707 RTMemFree(pRawDesc);
3708 return VINF_SUCCESS;
3709}
3710
3711/**
3712 * Helper that grows the raw partition descriptor table by @a cToAdd entries,
3713 * returning the pointer to the first new entry.
3714 * @internal
3715 */
3716static int vmdkRawDescAppendPartDesc(PVMDKIMAGE pImage, PVDISKRAW pRawDesc, uint32_t cToAdd, PVDISKRAWPARTDESC *ppRet)
3717{
3718 uint32_t const cOld = pRawDesc->cPartDescs;
3719 uint32_t const cNew = cOld + cToAdd;
3720 PVDISKRAWPARTDESC paNew = (PVDISKRAWPARTDESC)RTMemReallocZ(pRawDesc->pPartDescs,
3721 cOld * sizeof(pRawDesc->pPartDescs[0]),
3722 cNew * sizeof(pRawDesc->pPartDescs[0]));
3723 if (paNew)
3724 {
3725 pRawDesc->cPartDescs = cNew;
3726 pRawDesc->pPartDescs = paNew;
3727
3728 *ppRet = &paNew[cOld];
3729 return VINF_SUCCESS;
3730 }
3731 *ppRet = NULL;
3732 return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
3733 N_("VMDK: Image path: '%s'. Out of memory growing the partition descriptors (%u -> %u)."),
3734 pImage->pszFilename, cOld, cNew);
3735}
3736
3737/**
3738 * @callback_method_impl{FNRTSORTCMP}
3739 */
3740static DECLCALLBACK(int) vmdkRawDescPartComp(void const *pvElement1, void const *pvElement2, void *pvUser)
3741{
3742 RT_NOREF(pvUser);
3743 int64_t const iDelta = ((PVDISKRAWPARTDESC)pvElement1)->offStartInVDisk - ((PVDISKRAWPARTDESC)pvElement2)->offStartInVDisk;
3744 return iDelta < 0 ? -1 : iDelta > 0 ? 1 : 0;
3745}
3746
3747/**
3748 * Post processes the partition descriptors.
3749 *
3750 * Sorts them and check that they don't overlap.
3751 */
3752static int vmdkRawDescPostProcessPartitions(PVMDKIMAGE pImage, PVDISKRAW pRawDesc, uint64_t cbSize)
3753{
3754 /*
3755 * Sort data areas in ascending order of start.
3756 */
3757 RTSortShell(pRawDesc->pPartDescs, pRawDesc->cPartDescs, sizeof(pRawDesc->pPartDescs[0]), vmdkRawDescPartComp, NULL);
3758
3759 /*
3760 * Check that we don't have overlapping descriptors. If we do, that's an
3761 * indication that the drive is corrupt or that the RTDvm code is buggy.
3762 */
3763 VDISKRAWPARTDESC const *paPartDescs = pRawDesc->pPartDescs;
3764 for (uint32_t i = 0; i < pRawDesc->cPartDescs; i++)
3765 {
3766 uint64_t offLast = paPartDescs[i].offStartInVDisk + paPartDescs[i].cbData;
3767 if (offLast <= paPartDescs[i].offStartInVDisk)
3768 return vdIfError(pImage->pIfError, VERR_FILESYSTEM_CORRUPT /*?*/, RT_SRC_POS,
3769 N_("VMDK: Image path: '%s'. Bogus partition descriptor #%u (%#RX64 LB %#RX64%s): Wrap around or zero"),
3770 pImage->pszFilename, i, paPartDescs[i].offStartInVDisk, paPartDescs[i].cbData,
3771 paPartDescs[i].pvPartitionData ? " (data)" : "");
3772 offLast -= 1;
3773
3774 if (i + 1 < pRawDesc->cPartDescs && offLast >= paPartDescs[i + 1].offStartInVDisk)
3775 return vdIfError(pImage->pIfError, VERR_FILESYSTEM_CORRUPT /*?*/, RT_SRC_POS,
3776 N_("VMDK: Image path: '%s'. Partition descriptor #%u (%#RX64 LB %#RX64%s) overlaps with the next (%#RX64 LB %#RX64%s)"),
3777 pImage->pszFilename, i, paPartDescs[i].offStartInVDisk, paPartDescs[i].cbData,
3778 paPartDescs[i].pvPartitionData ? " (data)" : "", paPartDescs[i + 1].offStartInVDisk,
3779 paPartDescs[i + 1].cbData, paPartDescs[i + 1].pvPartitionData ? " (data)" : "");
3780 if (offLast >= cbSize)
3781 return vdIfError(pImage->pIfError, VERR_FILESYSTEM_CORRUPT /*?*/, RT_SRC_POS,
3782 N_("VMDK: Image path: '%s'. Partition descriptor #%u (%#RX64 LB %#RX64%s) goes beyond the end of the drive (%#RX64)"),
3783 pImage->pszFilename, i, paPartDescs[i].offStartInVDisk, paPartDescs[i].cbData,
3784 paPartDescs[i].pvPartitionData ? " (data)" : "", cbSize);
3785 }
3786
3787 return VINF_SUCCESS;
3788}
3789
3790
3791#ifdef RT_OS_LINUX
3792/**
3793 * Searches the dir specified in @a pszBlockDevDir for subdirectories with a
3794 * 'dev' file matching @a uDevToLocate.
3795 *
3796 * This is used both
3797 *
3798 * @returns IPRT status code, errors have been reported properly.
3799 * @param pImage For error reporting.
3800 * @param pszBlockDevDir Input: Path to the directory search under.
3801 * Output: Path to the directory containing information
3802 * for @a uDevToLocate.
3803 * @param cbBlockDevDir The size of the buffer @a pszBlockDevDir points to.
3804 * @param uDevToLocate The device number of the block device info dir to
3805 * locate.
3806 * @param pszDevToLocate For error reporting.
3807 */
3808static int vmdkFindSysBlockDevPath(PVMDKIMAGE pImage, char *pszBlockDevDir, size_t cbBlockDevDir,
3809 dev_t uDevToLocate, const char *pszDevToLocate)
3810{
3811 size_t const cchDir = RTPathEnsureTrailingSeparator(pszBlockDevDir, cbBlockDevDir);
3812 AssertReturn(cchDir > 0, VERR_BUFFER_OVERFLOW);
3813
3814 RTDIR hDir = NIL_RTDIR;
3815 int rc = RTDirOpen(&hDir, pszBlockDevDir);
3816 if (RT_SUCCESS(rc))
3817 {
3818 for (;;)
3819 {
3820 RTDIRENTRY Entry;
3821 rc = RTDirRead(hDir, &Entry, NULL);
3822 if (RT_SUCCESS(rc))
3823 {
3824 /* We're interested in directories and symlinks. */
3825 if ( Entry.enmType == RTDIRENTRYTYPE_DIRECTORY
3826 || Entry.enmType == RTDIRENTRYTYPE_SYMLINK
3827 || Entry.enmType == RTDIRENTRYTYPE_UNKNOWN)
3828 {
3829 rc = RTStrCopy(&pszBlockDevDir[cchDir], cbBlockDevDir - cchDir, Entry.szName);
3830 AssertContinue(RT_SUCCESS(rc)); /* should not happen! */
3831
3832 dev_t uThisDevNo = ~uDevToLocate;
3833 rc = RTLinuxSysFsReadDevNumFile(&uThisDevNo, "%s/dev", pszBlockDevDir);
3834 if (RT_SUCCESS(rc) && uThisDevNo == uDevToLocate)
3835 break;
3836 }
3837 }
3838 else
3839 {
3840 pszBlockDevDir[cchDir] = '\0';
3841 if (rc == VERR_NO_MORE_FILES)
3842 rc = vdIfError(pImage->pIfError, VERR_NOT_FOUND, RT_SRC_POS,
3843 N_("VMDK: Image path: '%s'. Failed to locate device corresponding to '%s' under '%s'"),
3844 pImage->pszFilename, pszDevToLocate, pszBlockDevDir);
3845 else
3846 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
3847 N_("VMDK: Image path: '%s'. RTDirRead failed enumerating '%s': %Rrc"),
3848 pImage->pszFilename, pszBlockDevDir, rc);
3849 break;
3850 }
3851 }
3852 RTDirClose(hDir);
3853 }
3854 else
3855 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
3856 N_("VMDK: Image path: '%s'. Failed to open dir '%s' for listing: %Rrc"),
3857 pImage->pszFilename, pszBlockDevDir, rc);
3858 return rc;
3859}
3860#endif /* RT_OS_LINUX */
3861
3862#ifdef RT_OS_FREEBSD
3863
3864
3865/**
3866 * Reads the config data from the provider and returns offset and size
3867 *
3868 * @return IPRT status code
3869 * @param pProvider GEOM provider representing partition
3870 * @param pcbOffset Placeholder for the offset of the partition
3871 * @param pcbSize Placeholder for the size of the partition
3872 */
3873static int vmdkReadPartitionsParamsFromProvider(gprovider *pProvider, uint64_t *pcbOffset, uint64_t *pcbSize)
3874{
3875 gconfig *pConfEntry;
3876 int rc = VERR_NOT_FOUND;
3877
3878 /*
3879 * Required parameters are located in the list containing key/value pairs.
3880 * Both key and value are in text form. Manuals tells nothing about the fact
3881 * that the both parameters should be present in the list. Thus, there are
3882 * cases when only one parameter is presented. To handle such cases we treat
3883 * absent params as zero allowing the caller decide the case is either correct
3884 * or an error.
3885 */
3886 uint64_t cbOffset = 0;
3887 uint64_t cbSize = 0;
3888 LIST_FOREACH(pConfEntry, &pProvider->lg_config, lg_config)
3889 {
3890 if (RTStrCmp(pConfEntry->lg_name, "offset") == 0)
3891 {
3892 cbOffset = RTStrToUInt64(pConfEntry->lg_val);
3893 rc = VINF_SUCCESS;
3894 }
3895 else if (RTStrCmp(pConfEntry->lg_name, "length") == 0)
3896 {
3897 cbSize = RTStrToUInt64(pConfEntry->lg_val);
3898 rc = VINF_SUCCESS;
3899 }
3900 }
3901 if (RT_SUCCESS(rc))
3902 {
3903 *pcbOffset = cbOffset;
3904 *pcbSize = cbSize;
3905 }
3906 return rc;
3907}
3908
3909
3910/**
3911 * Searches the partition specified by name and calculates its size and absolute offset.
3912 *
3913 * @return IPRT status code.
3914 * @param pParentClass Class containing pParentGeom
3915 * @param pszParentGeomName Name of the parent geom where we are looking for provider
3916 * @param pszProviderName Name of the provider we are looking for
3917 * @param pcbAbsoluteOffset Placeholder for the absolute offset of the partition, i.e. offset from the beginning of the disk
3918 * @param psbSize Placeholder for the size of the partition.
3919 */
3920static int vmdkFindPartitionParamsByName(gclass *pParentClass, const char *pszParentGeomName, const char *pszProviderName,
3921 uint64_t *pcbAbsoluteOffset, uint64_t *pcbSize)
3922{
3923 AssertReturn(pParentClass, VERR_INVALID_PARAMETER);
3924 AssertReturn(pszParentGeomName, VERR_INVALID_PARAMETER);
3925 AssertReturn(pszProviderName, VERR_INVALID_PARAMETER);
3926 AssertReturn(pcbAbsoluteOffset, VERR_INVALID_PARAMETER);
3927 AssertReturn(pcbSize, VERR_INVALID_PARAMETER);
3928
3929 ggeom *pParentGeom;
3930 int rc = VERR_NOT_FOUND;
3931 LIST_FOREACH(pParentGeom, &pParentClass->lg_geom, lg_geom)
3932 {
3933 if (RTStrCmp(pParentGeom->lg_name, pszParentGeomName) == 0)
3934 {
3935 rc = VINF_SUCCESS;
3936 break;
3937 }
3938 }
3939 if (RT_FAILURE(rc))
3940 return rc;
3941
3942 gprovider *pProvider;
3943 /*
3944 * First, go over providers without handling EBR or BSDLabel
3945 * partitions for case when looking provider is child
3946 * of the givng geom, to reduce searching time
3947 */
3948 LIST_FOREACH(pProvider, &pParentGeom->lg_provider, lg_provider)
3949 {
3950 if (RTStrCmp(pProvider->lg_name, pszProviderName) == 0)
3951 return vmdkReadPartitionsParamsFromProvider(pProvider, pcbAbsoluteOffset, pcbSize);
3952 }
3953
3954 /*
3955 * No provider found. Go over the parent geom again
3956 * and make recursions if geom represents EBR or BSDLabel.
3957 * In this case given parent geom contains only EBR or BSDLabel
3958 * partition itself and their own partitions are in the separate
3959 * geoms. Also, partition offsets are relative to geom, so
3960 * we have to add offset from child provider with parent geoms
3961 * provider
3962 */
3963
3964 LIST_FOREACH(pProvider, &pParentGeom->lg_provider, lg_provider)
3965 {
3966 uint64_t cbOffset = 0;
3967 uint64_t cbSize = 0;
3968 rc = vmdkReadPartitionsParamsFromProvider(pProvider, &cbOffset, &cbSize);
3969 if (RT_FAILURE(rc))
3970 return rc;
3971
3972 uint64_t cbProviderOffset = 0;
3973 uint64_t cbProviderSize = 0;
3974 rc = vmdkFindPartitionParamsByName(pParentClass, pProvider->lg_name, pszProviderName, &cbProviderOffset, &cbProviderSize);
3975 if (RT_SUCCESS(rc))
3976 {
3977 *pcbAbsoluteOffset = cbOffset + cbProviderOffset;
3978 *pcbSize = cbProviderSize;
3979 return rc;
3980 }
3981 }
3982
3983 return VERR_NOT_FOUND;
3984}
3985#endif
3986
3987
3988/**
3989 * Attempts to verify the raw partition path.
3990 *
3991 * We don't want to trust RTDvm and the partition device node morphing blindly.
3992 */
3993static int vmdkRawDescVerifyPartitionPath(PVMDKIMAGE pImage, PVDISKRAWPARTDESC pPartDesc, uint32_t idxPartition,
3994 const char *pszRawDrive, RTFILE hRawDrive, uint32_t cbSector, RTDVMVOLUME hVol)
3995{
3996 RT_NOREF(pImage, pPartDesc, idxPartition, pszRawDrive, hRawDrive, cbSector, hVol);
3997
3998 /*
3999 * Try open the raw partition device.
4000 */
4001 RTFILE hRawPart = NIL_RTFILE;
4002 int rc = RTFileOpen(&hRawPart, pPartDesc->pszRawDevice, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
4003 if (RT_FAILURE(rc))
4004 return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4005 N_("VMDK: Image path: '%s'. Failed to open partition #%u on '%s' via '%s' (%Rrc)"),
4006 pImage->pszFilename, idxPartition, pszRawDrive, pPartDesc->pszRawDevice, rc);
4007
4008 /*
4009 * Compare the partition UUID if we can get it.
4010 */
4011#ifdef RT_OS_WINDOWS
4012 DWORD cbReturned;
4013
4014 /* 1. Get the device numbers for both handles, they should have the same disk. */
4015 STORAGE_DEVICE_NUMBER DevNum1;
4016 RT_ZERO(DevNum1);
4017 if (!DeviceIoControl((HANDLE)RTFileToNative(hRawDrive), IOCTL_STORAGE_GET_DEVICE_NUMBER,
4018 NULL /*pvInBuffer*/, 0 /*cbInBuffer*/, &DevNum1, sizeof(DevNum1), &cbReturned, NULL /*pOverlapped*/))
4019 rc = vdIfError(pImage->pIfError, RTErrConvertFromWin32(GetLastError()), RT_SRC_POS,
4020 N_("VMDK: Image path: '%s'. IOCTL_STORAGE_GET_DEVICE_NUMBER failed on '%s': %u"),
4021 pImage->pszFilename, pszRawDrive, GetLastError());
4022
4023 STORAGE_DEVICE_NUMBER DevNum2;
4024 RT_ZERO(DevNum2);
4025 if (!DeviceIoControl((HANDLE)RTFileToNative(hRawPart), IOCTL_STORAGE_GET_DEVICE_NUMBER,
4026 NULL /*pvInBuffer*/, 0 /*cbInBuffer*/, &DevNum2, sizeof(DevNum2), &cbReturned, NULL /*pOverlapped*/))
4027 rc = vdIfError(pImage->pIfError, RTErrConvertFromWin32(GetLastError()), RT_SRC_POS,
4028 N_("VMDK: Image path: '%s'. IOCTL_STORAGE_GET_DEVICE_NUMBER failed on '%s': %u"),
4029 pImage->pszFilename, pPartDesc->pszRawDevice, GetLastError());
4030 if ( RT_SUCCESS(rc)
4031 && ( DevNum1.DeviceNumber != DevNum2.DeviceNumber
4032 || DevNum1.DeviceType != DevNum2.DeviceType))
4033 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4034 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s' (%#x != %#x || %#x != %#x)"),
4035 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive,
4036 DevNum1.DeviceNumber, DevNum2.DeviceNumber, DevNum1.DeviceType, DevNum2.DeviceType);
4037 if (RT_SUCCESS(rc))
4038 {
4039 /* Get the partitions from the raw drive and match up with the volume info
4040 from RTDvm. The partition number is found in DevNum2. */
4041 DWORD cbNeeded = 0;
4042 if ( DeviceIoControl((HANDLE)RTFileToNative(hRawDrive), IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
4043 NULL /*pvInBuffer*/, 0 /*cbInBuffer*/, NULL, 0, &cbNeeded, NULL /*pOverlapped*/)
4044 || cbNeeded < RT_UOFFSETOF_DYN(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[1]))
4045 cbNeeded = RT_UOFFSETOF_DYN(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[64]);
4046 cbNeeded += sizeof(PARTITION_INFORMATION_EX) * 2; /* just in case */
4047 DRIVE_LAYOUT_INFORMATION_EX *pLayout = (DRIVE_LAYOUT_INFORMATION_EX *)RTMemTmpAllocZ(cbNeeded);
4048 if (pLayout)
4049 {
4050 cbReturned = 0;
4051 if (DeviceIoControl((HANDLE)RTFileToNative(hRawDrive), IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
4052 NULL /*pvInBuffer*/, 0 /*cbInBuffer*/, pLayout, cbNeeded, &cbReturned, NULL /*pOverlapped*/))
4053 {
4054 /* Find the entry with the given partition number (it's not an index, array contains empty MBR entries ++). */
4055 unsigned iEntry = 0;
4056 while ( iEntry < pLayout->PartitionCount
4057 && pLayout->PartitionEntry[iEntry].PartitionNumber != DevNum2.PartitionNumber)
4058 iEntry++;
4059 if (iEntry < pLayout->PartitionCount)
4060 {
4061 /* Compare the basics */
4062 PARTITION_INFORMATION_EX const * const pLayoutEntry = &pLayout->PartitionEntry[iEntry];
4063 if (pLayoutEntry->StartingOffset.QuadPart != (int64_t)pPartDesc->offStartInVDisk)
4064 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4065 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': StartingOffset %RU64, expected %RU64"),
4066 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive,
4067 pLayoutEntry->StartingOffset.QuadPart, pPartDesc->offStartInVDisk);
4068 else if (pLayoutEntry->PartitionLength.QuadPart != (int64_t)pPartDesc->cbData)
4069 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4070 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': PartitionLength %RU64, expected %RU64"),
4071 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive,
4072 pLayoutEntry->PartitionLength.QuadPart, pPartDesc->cbData);
4073 /** @todo We could compare the MBR type, GPT type and ID. */
4074 RT_NOREF(hVol);
4075 }
4076 else
4077 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4078 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': PartitionCount (%#x vs %#x)"),
4079 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive,
4080 DevNum2.PartitionNumber, pLayout->PartitionCount);
4081# ifndef LOG_ENABLED
4082 if (RT_FAILURE(rc))
4083# endif
4084 {
4085 LogRel(("VMDK: Windows reports %u partitions for '%s':\n", pLayout->PartitionCount, pszRawDrive));
4086 PARTITION_INFORMATION_EX const *pEntry = &pLayout->PartitionEntry[0];
4087 for (DWORD i = 0; i < pLayout->PartitionCount; i++, pEntry++)
4088 {
4089 LogRel(("VMDK: #%u/%u: %016RU64 LB %016RU64 style=%d rewrite=%d",
4090 i, pEntry->PartitionNumber, pEntry->StartingOffset.QuadPart, pEntry->PartitionLength.QuadPart,
4091 pEntry->PartitionStyle, pEntry->RewritePartition));
4092 if (pEntry->PartitionStyle == PARTITION_STYLE_MBR)
4093 LogRel((" type=%#x boot=%d rec=%d hidden=%u\n", pEntry->Mbr.PartitionType, pEntry->Mbr.BootIndicator,
4094 pEntry->Mbr.RecognizedPartition, pEntry->Mbr.HiddenSectors));
4095 else if (pEntry->PartitionStyle == PARTITION_STYLE_GPT)
4096 LogRel((" type=%RTuuid id=%RTuuid aatrib=%RX64 name=%.36ls\n", &pEntry->Gpt.PartitionType,
4097 &pEntry->Gpt.PartitionId, pEntry->Gpt.Attributes, &pEntry->Gpt.Name[0]));
4098 else
4099 LogRel(("\n"));
4100 }
4101 LogRel(("VMDK: Looked for partition #%u (%u, '%s') at %RU64 LB %RU64\n", DevNum2.PartitionNumber,
4102 idxPartition, pPartDesc->pszRawDevice, pPartDesc->offStartInVDisk, pPartDesc->cbData));
4103 }
4104 }
4105 else
4106 rc = vdIfError(pImage->pIfError, RTErrConvertFromWin32(GetLastError()), RT_SRC_POS,
4107 N_("VMDK: Image path: '%s'. IOCTL_DISK_GET_DRIVE_LAYOUT_EX failed on '%s': %u (cb %u, cbRet %u)"),
4108 pImage->pszFilename, pPartDesc->pszRawDevice, GetLastError(), cbNeeded, cbReturned);
4109 RTMemTmpFree(pLayout);
4110 }
4111 else
4112 rc = VERR_NO_TMP_MEMORY;
4113 }
4114
4115#elif defined(RT_OS_LINUX)
4116 RT_NOREF(hVol);
4117
4118 /* Stat the two devices first to get their device numbers. (We probably
4119 could make some assumptions here about the major & minor number assignments
4120 for legacy nodes, but it doesn't hold up for nvme, so we'll skip that.) */
4121 struct stat StDrive, StPart;
4122 if (fstat((int)RTFileToNative(hRawDrive), &StDrive) != 0)
4123 rc = vdIfError(pImage->pIfError, RTErrConvertFromErrno(errno), RT_SRC_POS,
4124 N_("VMDK: Image path: '%s'. fstat failed on '%s': %d"), pImage->pszFilename, pszRawDrive, errno);
4125 else if (fstat((int)RTFileToNative(hRawPart), &StPart) != 0)
4126 rc = vdIfError(pImage->pIfError, RTErrConvertFromErrno(errno), RT_SRC_POS,
4127 N_("VMDK: Image path: '%s'. fstat failed on '%s': %d"), pImage->pszFilename, pPartDesc->pszRawDevice, errno);
4128 else
4129 {
4130 /* Scan the directories immediately under /sys/block/ for one with a
4131 'dev' file matching the drive's device number: */
4132 char szSysPath[RTPATH_MAX];
4133 rc = RTLinuxConstructPath(szSysPath, sizeof(szSysPath), "block/");
4134 AssertRCReturn(rc, rc); /* this shall not fail */
4135 if (RTDirExists(szSysPath))
4136 {
4137 rc = vmdkFindSysBlockDevPath(pImage, szSysPath, sizeof(szSysPath), StDrive.st_rdev, pszRawDrive);
4138
4139 /* Now, scan the directories under that again for a partition device
4140 matching the hRawPart device's number: */
4141 if (RT_SUCCESS(rc))
4142 rc = vmdkFindSysBlockDevPath(pImage, szSysPath, sizeof(szSysPath), StPart.st_rdev, pPartDesc->pszRawDevice);
4143
4144 /* Having found the /sys/block/device/partition/ path, we can finally
4145 read the partition attributes and compare with hVol. */
4146 if (RT_SUCCESS(rc))
4147 {
4148 /* partition number: */
4149 int64_t iLnxPartition = 0;
4150 rc = RTLinuxSysFsReadIntFile(10, &iLnxPartition, "%s/partition", szSysPath);
4151 if (RT_SUCCESS(rc) && iLnxPartition != idxPartition)
4152 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4153 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': Partition number %RI64, expected %RU32"),
4154 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, iLnxPartition, idxPartition);
4155 /* else: ignore failure? */
4156
4157 /* start offset: */
4158 uint32_t const cbLnxSector = 512; /* It's hardcoded in the Linux kernel */
4159 if (RT_SUCCESS(rc))
4160 {
4161 int64_t offLnxStart = -1;
4162 rc = RTLinuxSysFsReadIntFile(10, &offLnxStart, "%s/start", szSysPath);
4163 offLnxStart *= cbLnxSector;
4164 if (RT_SUCCESS(rc) && offLnxStart != (int64_t)pPartDesc->offStartInVDisk)
4165 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4166 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': Start offset %RI64, expected %RU64"),
4167 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, offLnxStart, pPartDesc->offStartInVDisk);
4168 /* else: ignore failure? */
4169 }
4170
4171 /* the size: */
4172 if (RT_SUCCESS(rc))
4173 {
4174 int64_t cbLnxData = -1;
4175 rc = RTLinuxSysFsReadIntFile(10, &cbLnxData, "%s/size", szSysPath);
4176 cbLnxData *= cbLnxSector;
4177 if (RT_SUCCESS(rc) && cbLnxData != (int64_t)pPartDesc->cbData)
4178 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4179 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': Size %RI64, expected %RU64"),
4180 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, cbLnxData, pPartDesc->cbData);
4181 /* else: ignore failure? */
4182 }
4183 }
4184 }
4185 /* else: We've got nothing to work on, so only do content comparison. */
4186 }
4187
4188#elif defined(RT_OS_FREEBSD)
4189 char szDriveDevName[256];
4190 char* pszDevName = fdevname_r(RTFileToNative(hRawDrive), szDriveDevName, 256);
4191 if (pszDevName == NULL)
4192 rc = vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
4193 N_("VMDK: Image path: '%s'. '%s' is not a drive path"), pImage->pszFilename, pszRawDrive);
4194 char szPartDevName[256];
4195 if (RT_SUCCESS(rc))
4196 {
4197 pszDevName = fdevname_r(RTFileToNative(hRawPart), szPartDevName, 256);
4198 if (pszDevName == NULL)
4199 rc = vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
4200 N_("VMDK: Image path: '%s'. '%s' is not a partition path"), pImage->pszFilename, pPartDesc->pszRawDevice);
4201 }
4202 if (RT_SUCCESS(rc))
4203 {
4204 gmesh geomMesh;
4205 int err = geom_gettree(&geomMesh);
4206 if (err == 0)
4207 {
4208 /* Find root class containg partitions info */
4209 gclass* pPartClass;
4210 LIST_FOREACH(pPartClass, &geomMesh.lg_class, lg_class)
4211 {
4212 if (RTStrCmp(pPartClass->lg_name, "PART") == 0)
4213 break;
4214 }
4215 if (pPartClass == NULL || RTStrCmp(pPartClass->lg_name, "PART") != 0)
4216 rc = vdIfError(pImage->pIfError, VERR_GENERAL_FAILURE, RT_SRC_POS,
4217 N_("VMDK: Image path: '%s'. 'PART' class not found in the GEOM tree"), pImage->pszFilename);
4218
4219
4220 if (RT_SUCCESS(rc))
4221 {
4222 /* Find provider representing partition device */
4223 uint64_t cbOffset;
4224 uint64_t cbSize;
4225 rc = vmdkFindPartitionParamsByName(pPartClass, szDriveDevName, szPartDevName, &cbOffset, &cbSize);
4226 if (RT_SUCCESS(rc))
4227 {
4228 if (cbOffset != pPartDesc->offStartInVDisk)
4229 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4230 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': Start offset %RU64, expected %RU64"),
4231 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, cbOffset, pPartDesc->offStartInVDisk);
4232 if (cbSize != pPartDesc->cbData)
4233 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4234 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': Size %RU64, expected %RU64"),
4235 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, cbSize, pPartDesc->cbData);
4236 }
4237 else
4238 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4239 N_("VMDK: Image path: '%s'. Error getting geom provider for the partition '%s' of the drive '%s' in the GEOM tree: %Rrc"),
4240 pImage->pszFilename, pPartDesc->pszRawDevice, pszRawDrive, rc);
4241 }
4242
4243 geom_deletetree(&geomMesh);
4244 }
4245 else
4246 rc = vdIfError(pImage->pIfError, RTErrConvertFromErrno(err), RT_SRC_POS,
4247 N_("VMDK: Image path: '%s'. geom_gettree failed: %d"), pImage->pszFilename, err);
4248 }
4249
4250#elif defined(RT_OS_SOLARIS)
4251 RT_NOREF(hVol);
4252
4253 dk_cinfo dkiDriveInfo;
4254 dk_cinfo dkiPartInfo;
4255 if (ioctl(RTFileToNative(hRawDrive), DKIOCINFO, (caddr_t)&dkiDriveInfo) == -1)
4256 rc = vdIfError(pImage->pIfError, RTErrConvertFromErrno(errno), RT_SRC_POS,
4257 N_("VMDK: Image path: '%s'. DKIOCINFO failed on '%s': %d"), pImage->pszFilename, pszRawDrive, errno);
4258 else if (ioctl(RTFileToNative(hRawPart), DKIOCINFO, (caddr_t)&dkiPartInfo) == -1)
4259 rc = vdIfError(pImage->pIfError, RTErrConvertFromErrno(errno), RT_SRC_POS,
4260 N_("VMDK: Image path: '%s'. DKIOCINFO failed on '%s': %d"), pImage->pszFilename, pszRawDrive, errno);
4261 else if ( dkiDriveInfo.dki_ctype != dkiPartInfo.dki_ctype
4262 || dkiDriveInfo.dki_cnum != dkiPartInfo.dki_cnum
4263 || dkiDriveInfo.dki_addr != dkiPartInfo.dki_addr
4264 || dkiDriveInfo.dki_unit != dkiPartInfo.dki_unit
4265 || dkiDriveInfo.dki_slave != dkiPartInfo.dki_slave)
4266 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4267 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s' (%#x != %#x || %#x != %#x || %#x != %#x || %#x != %#x || %#x != %#x)"),
4268 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive,
4269 dkiDriveInfo.dki_ctype, dkiPartInfo.dki_ctype, dkiDriveInfo.dki_cnum, dkiPartInfo.dki_cnum,
4270 dkiDriveInfo.dki_addr, dkiPartInfo.dki_addr, dkiDriveInfo.dki_unit, dkiPartInfo.dki_unit,
4271 dkiDriveInfo.dki_slave, dkiPartInfo.dki_slave);
4272 else
4273 {
4274 uint64_t cbOffset = 0;
4275 uint64_t cbSize = 0;
4276 dk_gpt *pEfi = NULL;
4277 int idxEfiPart = efi_alloc_and_read(RTFileToNative(hRawPart), &pEfi);
4278 if (idxEfiPart >= 0)
4279 {
4280 if ((uint32_t)dkiPartInfo.dki_partition + 1 == idxPartition)
4281 {
4282 cbOffset = pEfi->efi_parts[idxEfiPart].p_start * pEfi->efi_lbasize;
4283 cbSize = pEfi->efi_parts[idxEfiPart].p_size * pEfi->efi_lbasize;
4284 }
4285 else
4286 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4287 N_("VMDK: Image path: '%s'. Partition #%u number ('%s') verification failed on '%s' (%#x != %#x)"),
4288 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive,
4289 idxPartition, (uint32_t)dkiPartInfo.dki_partition + 1);
4290 efi_free(pEfi);
4291 }
4292 else
4293 {
4294 /*
4295 * Manual says the efi_alloc_and_read returns VT_EINVAL if no EFI partition table found.
4296 * Actually, the function returns any error, e.g. VT_ERROR. Thus, we are not sure, is it
4297 * real error or just no EFI table found. Therefore, let's try to obtain partition info
4298 * using another way. If there is an error, it returns errno which will be handled below.
4299 */
4300
4301 uint32_t numPartition = (uint32_t)dkiPartInfo.dki_partition;
4302 if (numPartition > NDKMAP)
4303 numPartition -= NDKMAP;
4304 if (numPartition != idxPartition)
4305 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4306 N_("VMDK: Image path: '%s'. Partition #%u number ('%s') verification failed on '%s' (%#x != %#x)"),
4307 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive,
4308 idxPartition, numPartition);
4309 else
4310 {
4311 dk_minfo_ext mediaInfo;
4312 if (ioctl(RTFileToNative(hRawPart), DKIOCGMEDIAINFOEXT, (caddr_t)&mediaInfo) == -1)
4313 rc = vdIfError(pImage->pIfError, RTErrConvertFromErrno(errno), RT_SRC_POS,
4314 N_("VMDK: Image path: '%s'. Partition #%u number ('%s') verification failed on '%s'. Can not obtain partition info: %d"),
4315 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, errno);
4316 else
4317 {
4318 extpart_info extPartInfo;
4319 if (ioctl(RTFileToNative(hRawPart), DKIOCEXTPARTINFO, (caddr_t)&extPartInfo) != -1)
4320 {
4321 cbOffset = (uint64_t)extPartInfo.p_start * mediaInfo.dki_lbsize;
4322 cbSize = (uint64_t)extPartInfo.p_length * mediaInfo.dki_lbsize;
4323 }
4324 else
4325 rc = vdIfError(pImage->pIfError, RTErrConvertFromErrno(errno), RT_SRC_POS,
4326 N_("VMDK: Image path: '%s'. Partition #%u number ('%s') verification failed on '%s'. Can not obtain partition info: %d"),
4327 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, errno);
4328 }
4329 }
4330 }
4331 if (RT_SUCCESS(rc) && cbOffset != pPartDesc->offStartInVDisk)
4332 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4333 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': Start offset %RI64, expected %RU64"),
4334 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, cbOffset, pPartDesc->offStartInVDisk);
4335
4336 if (RT_SUCCESS(rc) && cbSize != pPartDesc->cbData)
4337 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4338 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': Size %RI64, expected %RU64"),
4339 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, cbSize, pPartDesc->cbData);
4340 }
4341
4342#elif defined(RT_OS_DARWIN)
4343 /* Stat the drive get its device number. */
4344 struct stat StDrive;
4345 if (fstat((int)RTFileToNative(hRawDrive), &StDrive) != 0)
4346 rc = vdIfError(pImage->pIfError, RTErrConvertFromErrno(errno), RT_SRC_POS,
4347 N_("VMDK: Image path: '%s'. fstat failed on '%s' (errno=%d)"), pImage->pszFilename, pszRawDrive, errno);
4348 else
4349 {
4350 if (ioctl(RTFileToNative(hRawPart), DKIOCLOCKPHYSICALEXTENTS, NULL) == -1)
4351 rc = vdIfError(pImage->pIfError, RTErrConvertFromErrno(errno), RT_SRC_POS,
4352 N_("VMDK: Image path: '%s'. Partition #%u number ('%s') verification failed on '%s': Unable to lock the partition (errno=%d)"),
4353 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, errno);
4354 else
4355 {
4356 uint32_t cbBlockSize = 0;
4357 uint64_t cbOffset = 0;
4358 uint64_t cbSize = 0;
4359 if (ioctl(RTFileToNative(hRawPart), DKIOCGETBLOCKSIZE, (caddr_t)&cbBlockSize) == -1)
4360 rc = vdIfError(pImage->pIfError, RTErrConvertFromErrno(errno), RT_SRC_POS,
4361 N_("VMDK: Image path: '%s'. Partition #%u number ('%s') verification failed on '%s': Unable to obtain the sector size of the partition (errno=%d)"),
4362 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, errno);
4363 else if (ioctl(RTFileToNative(hRawPart), DKIOCGETBASE, (caddr_t)&cbOffset) == -1)
4364 rc = vdIfError(pImage->pIfError, RTErrConvertFromErrno(errno), RT_SRC_POS,
4365 N_("VMDK: Image path: '%s'. Partition #%u number ('%s') verification failed on '%s': Unable to obtain the start offset of the partition (errno=%d)"),
4366 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, errno);
4367 else if (ioctl(RTFileToNative(hRawPart), DKIOCGETBLOCKCOUNT, (caddr_t)&cbSize) == -1)
4368 rc = vdIfError(pImage->pIfError, RTErrConvertFromErrno(errno), RT_SRC_POS,
4369 N_("VMDK: Image path: '%s'. Partition #%u number ('%s') verification failed on '%s': Unable to obtain the size of the partition (errno=%d)"),
4370 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, errno);
4371 else
4372 {
4373 cbSize *= (uint64_t)cbBlockSize;
4374 dk_physical_extent_t dkPartExtent = {0};
4375 dkPartExtent.offset = 0;
4376 dkPartExtent.length = cbSize;
4377 if (ioctl(RTFileToNative(hRawPart), DKIOCGETPHYSICALEXTENT, (caddr_t)&dkPartExtent) == -1)
4378 rc = vdIfError(pImage->pIfError, RTErrConvertFromErrno(errno), RT_SRC_POS,
4379 N_("VMDK: Image path: '%s'. Partition #%u number ('%s') verification failed on '%s': Unable to obtain partition info (errno=%d)"),
4380 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, errno);
4381 else
4382 {
4383 if (dkPartExtent.dev != StDrive.st_rdev)
4384 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4385 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': Drive does not contain the partition"),
4386 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive);
4387 else if (cbOffset != pPartDesc->offStartInVDisk)
4388 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4389 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': Start offset %RU64, expected %RU64"),
4390 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, cbOffset, pPartDesc->offStartInVDisk);
4391 else if (cbSize != pPartDesc->cbData)
4392 rc = vdIfError(pImage->pIfError, VERR_MISMATCH, RT_SRC_POS,
4393 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s': Size %RU64, expected %RU64"),
4394 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, cbSize, pPartDesc->cbData);
4395 }
4396 }
4397
4398 if (ioctl(RTFileToNative(hRawPart), DKIOCUNLOCKPHYSICALEXTENTS, NULL) == -1)
4399 {
4400 int rc2 = vdIfError(pImage->pIfError, RTErrConvertFromErrno(errno), RT_SRC_POS,
4401 N_("VMDK: Image path: '%s'. Partition #%u number ('%s') verification failed on '%s': Unable to unlock the partition (errno=%d)"),
4402 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, errno);
4403 if (RT_SUCCESS(rc))
4404 rc = rc2;
4405 }
4406 }
4407 }
4408
4409#else
4410 RT_NOREF(hVol); /* PORTME */
4411 rc = VERR_NOT_SUPPORTED;
4412#endif
4413 if (RT_SUCCESS(rc))
4414 {
4415 /*
4416 * Compare the first 32 sectors of the partition.
4417 *
4418 * This might not be conclusive, but for partitions formatted with the more
4419 * common file systems it should be as they have a superblock copy at or near
4420 * the start of the partition (fat, fat32, ntfs, and ext4 does at least).
4421 */
4422 size_t const cbToCompare = (size_t)RT_MIN(pPartDesc->cbData / cbSector, 32) * cbSector;
4423 uint8_t *pbSector1 = (uint8_t *)RTMemTmpAlloc(cbToCompare * 2);
4424 if (pbSector1 != NULL)
4425 {
4426 uint8_t *pbSector2 = pbSector1 + cbToCompare;
4427
4428 /* Do the comparing, we repeat if it fails and the data might be volatile. */
4429 uint64_t uPrevCrc1 = 0;
4430 uint64_t uPrevCrc2 = 0;
4431 uint32_t cStable = 0;
4432 for (unsigned iTry = 0; iTry < 256; iTry++)
4433 {
4434 rc = RTFileReadAt(hRawDrive, pPartDesc->offStartInVDisk, pbSector1, cbToCompare, NULL);
4435 if (RT_SUCCESS(rc))
4436 {
4437 rc = RTFileReadAt(hRawPart, pPartDesc->offStartInDevice, pbSector2, cbToCompare, NULL);
4438 if (RT_SUCCESS(rc))
4439 {
4440 if (memcmp(pbSector1, pbSector2, cbToCompare) != 0)
4441 {
4442 rc = VERR_MISMATCH;
4443
4444 /* Do data stability checks before repeating: */
4445 uint64_t const uCrc1 = RTCrc64(pbSector1, cbToCompare);
4446 uint64_t const uCrc2 = RTCrc64(pbSector2, cbToCompare);
4447 if ( uPrevCrc1 != uCrc1
4448 || uPrevCrc2 != uCrc2)
4449 cStable = 0;
4450 else if (++cStable > 4)
4451 break;
4452 uPrevCrc1 = uCrc1;
4453 uPrevCrc2 = uCrc2;
4454 continue;
4455 }
4456 rc = VINF_SUCCESS;
4457 }
4458 else
4459 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4460 N_("VMDK: Image path: '%s'. Error reading %zu bytes from '%s' at offset %RU64 (%Rrc)"),
4461 pImage->pszFilename, cbToCompare, pPartDesc->pszRawDevice, pPartDesc->offStartInDevice, rc);
4462 }
4463 else
4464 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4465 N_("VMDK: Image path: '%s'. Error reading %zu bytes from '%s' at offset %RU64 (%Rrc)"),
4466 pImage->pszFilename, cbToCompare, pszRawDrive, pPartDesc->offStartInVDisk, rc);
4467 break;
4468 }
4469 if (rc == VERR_MISMATCH)
4470 {
4471 /* Find the first mismatching bytes: */
4472 size_t offMissmatch = 0;
4473 while (offMissmatch < cbToCompare && pbSector1[offMissmatch] == pbSector2[offMissmatch])
4474 offMissmatch++;
4475 int cbSample = (int)RT_MIN(cbToCompare - offMissmatch, 16);
4476
4477 if (cStable > 0)
4478 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4479 N_("VMDK: Image path: '%s'. Partition #%u path ('%s') verification failed on '%s' (cStable=%d @%#zx: %.*Rhxs vs %.*Rhxs)"),
4480 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive, cStable,
4481 offMissmatch, cbSample, &pbSector1[offMissmatch], cbSample, &pbSector2[offMissmatch]);
4482 else
4483 {
4484 LogRel(("VMDK: Image path: '%s'. Partition #%u path ('%s') verification undecided on '%s' because of unstable data! (@%#zx: %.*Rhxs vs %.*Rhxs)\n",
4485 pImage->pszFilename, idxPartition, pPartDesc->pszRawDevice, pszRawDrive,
4486 offMissmatch, cbSample, &pbSector1[offMissmatch], cbSample, &pbSector2[offMissmatch]));
4487 rc = -rc;
4488 }
4489 }
4490
4491 RTMemTmpFree(pbSector1);
4492 }
4493 else
4494 rc = vdIfError(pImage->pIfError, VERR_NO_TMP_MEMORY, RT_SRC_POS,
4495 N_("VMDK: Image path: '%s'. Failed to allocate %zu bytes for a temporary read buffer\n"),
4496 pImage->pszFilename, cbToCompare * 2);
4497 }
4498 RTFileClose(hRawPart);
4499 return rc;
4500}
4501
4502#ifdef RT_OS_WINDOWS
4503/**
4504 * Construct the device name for the given partition number.
4505 */
4506static int vmdkRawDescWinMakePartitionName(PVMDKIMAGE pImage, const char *pszRawDrive, RTFILE hRawDrive, uint32_t idxPartition,
4507 char **ppszRawPartition)
4508{
4509 int rc = VINF_SUCCESS;
4510 DWORD cbReturned = 0;
4511 STORAGE_DEVICE_NUMBER DevNum;
4512 RT_ZERO(DevNum);
4513 if (DeviceIoControl((HANDLE)RTFileToNative(hRawDrive), IOCTL_STORAGE_GET_DEVICE_NUMBER,
4514 NULL /*pvInBuffer*/, 0 /*cbInBuffer*/, &DevNum, sizeof(DevNum), &cbReturned, NULL /*pOverlapped*/))
4515 RTStrAPrintf(ppszRawPartition, "\\\\.\\Harddisk%uPartition%u", DevNum.DeviceNumber, idxPartition);
4516 else
4517 rc = vdIfError(pImage->pIfError, RTErrConvertFromWin32(GetLastError()), RT_SRC_POS,
4518 N_("VMDK: Image path: '%s'. IOCTL_STORAGE_GET_DEVICE_NUMBER failed on '%s': %u"),
4519 pImage->pszFilename, pszRawDrive, GetLastError());
4520 return rc;
4521}
4522#endif /* RT_OS_WINDOWS */
4523
4524/**
4525 * Worker for vmdkMakeRawDescriptor that adds partition descriptors when the
4526 * 'Partitions' configuration value is present.
4527 *
4528 * @returns VBox status code, error message has been set on failure.
4529 *
4530 * @note Caller is assumed to clean up @a pRawDesc and release
4531 * @a *phVolToRelease.
4532 * @internal
4533 */
4534static int vmdkRawDescDoPartitions(PVMDKIMAGE pImage, RTDVM hVolMgr, PVDISKRAW pRawDesc,
4535 RTFILE hRawDrive, const char *pszRawDrive, uint32_t cbSector,
4536 uint32_t fPartitions, uint32_t fPartitionsReadOnly, bool fRelative,
4537 PRTDVMVOLUME phVolToRelease)
4538{
4539 *phVolToRelease = NIL_RTDVMVOLUME;
4540
4541 /* Check sanity/understanding. */
4542 Assert(fPartitions);
4543 Assert((fPartitions & fPartitionsReadOnly) == fPartitionsReadOnly); /* RO should be a sub-set */
4544
4545 /*
4546 * Allocate on descriptor for each volume up front.
4547 */
4548 uint32_t const cVolumes = RTDvmMapGetValidVolumes(hVolMgr);
4549
4550 PVDISKRAWPARTDESC paPartDescs = NULL;
4551 int rc = vmdkRawDescAppendPartDesc(pImage, pRawDesc, cVolumes, &paPartDescs);
4552 AssertRCReturn(rc, rc);
4553
4554 /*
4555 * Enumerate the partitions (volumes) on the disk and create descriptors for each of them.
4556 */
4557 uint32_t fPartitionsLeft = fPartitions;
4558 RTDVMVOLUME hVol = NIL_RTDVMVOLUME; /* the current volume, needed for getting the next. */
4559 for (uint32_t i = 0; i < cVolumes; i++)
4560 {
4561 /*
4562 * Get the next/first volume and release the current.
4563 */
4564 RTDVMVOLUME hVolNext = NIL_RTDVMVOLUME;
4565 if (i == 0)
4566 rc = RTDvmMapQueryFirstVolume(hVolMgr, &hVolNext);
4567 else
4568 rc = RTDvmMapQueryNextVolume(hVolMgr, hVol, &hVolNext);
4569 if (RT_FAILURE(rc))
4570 return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4571 N_("VMDK: Image path: '%s'. Volume enumeration failed at volume #%u on '%s' (%Rrc)"),
4572 pImage->pszFilename, i, pszRawDrive, rc);
4573 uint32_t cRefs = RTDvmVolumeRelease(hVol);
4574 Assert(cRefs != UINT32_MAX); RT_NOREF(cRefs);
4575 *phVolToRelease = hVol = hVolNext;
4576
4577 /*
4578 * Depending on the fPartitions selector and associated read-only mask,
4579 * the guest either gets read-write or read-only access (bits set)
4580 * or no access (selector bit clear, access directed to the VMDK).
4581 */
4582 paPartDescs[i].cbData = RTDvmVolumeGetSize(hVol);
4583
4584 uint64_t offVolumeEndIgnored = 0;
4585 rc = RTDvmVolumeQueryRange(hVol, &paPartDescs[i].offStartInVDisk, &offVolumeEndIgnored);
4586 if (RT_FAILURE(rc))
4587 return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4588 N_("VMDK: Image path: '%s'. Failed to get location of volume #%u on '%s' (%Rrc)"),
4589 pImage->pszFilename, i, pszRawDrive, rc);
4590 Assert(paPartDescs[i].cbData == offVolumeEndIgnored + 1 - paPartDescs[i].offStartInVDisk);
4591
4592 /* Note! The index must match IHostDrivePartition::number. */
4593 uint32_t idxPartition = RTDvmVolumeGetIndex(hVol, RTDVMVOLIDX_HOST);
4594 if ( idxPartition < 32
4595 && (fPartitions & RT_BIT_32(idxPartition)))
4596 {
4597 fPartitionsLeft &= ~RT_BIT_32(idxPartition);
4598 if (fPartitionsReadOnly & RT_BIT_32(idxPartition))
4599 paPartDescs[i].uFlags |= VDISKRAW_READONLY;
4600
4601 if (!fRelative)
4602 {
4603 /*
4604 * Accessing the drive thru the main device node (pRawDesc->pszRawDisk).
4605 */
4606 paPartDescs[i].offStartInDevice = paPartDescs[i].offStartInVDisk;
4607 paPartDescs[i].pszRawDevice = RTStrDup(pszRawDrive);
4608 AssertPtrReturn(paPartDescs[i].pszRawDevice, VERR_NO_STR_MEMORY);
4609 }
4610 else
4611 {
4612 /*
4613 * Relative means access the partition data via the device node for that
4614 * partition, allowing the sysadmin/OS to allow a user access to individual
4615 * partitions without necessarily being able to compromise the host OS.
4616 * Obviously, the creation of the VMDK requires read access to the main
4617 * device node for the drive, but that's a one-time thing and can be done
4618 * by the sysadmin. Here data starts at offset zero in the device node.
4619 */
4620 paPartDescs[i].offStartInDevice = 0;
4621
4622#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
4623 /* /dev/rdisk1 -> /dev/rdisk1s2 (s=slice) */
4624 RTStrAPrintf(&paPartDescs[i].pszRawDevice, "%ss%u", pszRawDrive, idxPartition);
4625#elif defined(RT_OS_LINUX)
4626 /* Two naming schemes here: /dev/nvme0n1 -> /dev/nvme0n1p1; /dev/sda -> /dev/sda1 */
4627 RTStrAPrintf(&paPartDescs[i].pszRawDevice,
4628 RT_C_IS_DIGIT(pszRawDrive[strlen(pszRawDrive) - 1]) ? "%sp%u" : "%s%u", pszRawDrive, idxPartition);
4629#elif defined(RT_OS_WINDOWS)
4630 rc = vmdkRawDescWinMakePartitionName(pImage, pszRawDrive, hRawDrive, idxPartition, &paPartDescs[i].pszRawDevice);
4631 AssertRCReturn(rc, rc);
4632#elif defined(RT_OS_SOLARIS)
4633 if (pRawDesc->enmPartitioningType == VDISKPARTTYPE_MBR)
4634 {
4635 /*
4636 * MBR partitions have device nodes in form /dev/(r)dsk/cXtYdZpK
4637 * where X is the controller,
4638 * Y is target (SCSI device number),
4639 * Z is disk number,
4640 * K is partition number,
4641 * where p0 is the whole disk
4642 * p1-pN are the partitions of the disk
4643 */
4644 const char *pszRawDrivePath = pszRawDrive;
4645 char szDrivePath[RTPATH_MAX];
4646 size_t cbRawDrive = strlen(pszRawDrive);
4647 if ( cbRawDrive > 1 && strcmp(&pszRawDrive[cbRawDrive - 2], "p0") == 0)
4648 {
4649 memcpy(szDrivePath, pszRawDrive, cbRawDrive - 2);
4650 szDrivePath[cbRawDrive - 2] = '\0';
4651 pszRawDrivePath = szDrivePath;
4652 }
4653 RTStrAPrintf(&paPartDescs[i].pszRawDevice, "%sp%u", pszRawDrivePath, idxPartition);
4654 }
4655 else /* GPT */
4656 {
4657 /*
4658 * GPT partitions have device nodes in form /dev/(r)dsk/cXtYdZsK
4659 * where X is the controller,
4660 * Y is target (SCSI device number),
4661 * Z is disk number,
4662 * K is partition number, zero based. Can be only from 0 to 6.
4663 * Thus, only partitions numbered 0 through 6 have device nodes.
4664 */
4665 if (idxPartition > 7)
4666 return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
4667 N_("VMDK: Image path: '%s'. the partition #%u on '%s' has no device node and can not be specified with 'Relative' property"),
4668 pImage->pszFilename, idxPartition, pszRawDrive);
4669 RTStrAPrintf(&paPartDescs[i].pszRawDevice, "%ss%u", pszRawDrive, idxPartition - 1);
4670 }
4671#else
4672 AssertFailedReturn(VERR_INTERNAL_ERROR_4); /* The option parsing code should have prevented this - PORTME */
4673#endif
4674 AssertPtrReturn(paPartDescs[i].pszRawDevice, VERR_NO_STR_MEMORY);
4675
4676 rc = vmdkRawDescVerifyPartitionPath(pImage, &paPartDescs[i], idxPartition, pszRawDrive, hRawDrive, cbSector, hVol);
4677 AssertRCReturn(rc, rc);
4678 }
4679 }
4680 else
4681 {
4682 /* Not accessible to the guest. */
4683 paPartDescs[i].offStartInDevice = 0;
4684 paPartDescs[i].pszRawDevice = NULL;
4685 }
4686 } /* for each volume */
4687
4688 RTDvmVolumeRelease(hVol);
4689 *phVolToRelease = NIL_RTDVMVOLUME;
4690
4691 /*
4692 * Check that we found all the partitions the user selected.
4693 */
4694 if (fPartitionsLeft)
4695 {
4696 char szLeft[3 * sizeof(fPartitions) * 8];
4697 size_t cchLeft = 0;
4698 for (unsigned i = 0; i < sizeof(fPartitions) * 8; i++)
4699 if (fPartitionsLeft & RT_BIT_32(i))
4700 cchLeft += RTStrPrintf(&szLeft[cchLeft], sizeof(szLeft) - cchLeft, cchLeft ? "%u" : ",%u", i);
4701 return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
4702 N_("VMDK: Image path: '%s'. Not all the specified partitions for drive '%s' was found: %s"),
4703 pImage->pszFilename, pszRawDrive, szLeft);
4704 }
4705
4706 return VINF_SUCCESS;
4707}
4708
4709/**
4710 * Worker for vmdkMakeRawDescriptor that adds partition descriptors with copies
4711 * of the partition tables and associated padding areas when the 'Partitions'
4712 * configuration value is present.
4713 *
4714 * The guest is not allowed access to the partition tables, however it needs
4715 * them to be able to access the drive. So, create descriptors for each of the
4716 * tables and attach the current disk content. vmdkCreateRawImage() will later
4717 * write the content to the VMDK. Any changes the guest later makes to the
4718 * partition tables will then go to the VMDK copy, rather than the host drive.
4719 *
4720 * @returns VBox status code, error message has been set on failure.
4721 *
4722 * @note Caller is assumed to clean up @a pRawDesc
4723 * @internal
4724 */
4725static int vmdkRawDescDoCopyPartitionTables(PVMDKIMAGE pImage, RTDVM hVolMgr, PVDISKRAW pRawDesc,
4726 const char *pszRawDrive, RTFILE hRawDrive, void *pvBootSector, size_t cbBootSector)
4727{
4728 /*
4729 * Query the locations.
4730 */
4731 /* Determin how many locations there are: */
4732 size_t cLocations = 0;
4733 int rc = RTDvmMapQueryTableLocations(hVolMgr, RTDVMMAPQTABLOC_F_INCLUDE_LEGACY, NULL, 0, &cLocations);
4734 if (rc != VERR_BUFFER_OVERFLOW)
4735 return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4736 N_("VMDK: Image path: '%s'. RTDvmMapQueryTableLocations failed on '%s' (%Rrc)"),
4737 pImage->pszFilename, pszRawDrive, rc);
4738 AssertReturn(cLocations > 0 && cLocations < _16M, VERR_INTERNAL_ERROR_5);
4739
4740 /* We can allocate the partition descriptors here to save an intentation level. */
4741 PVDISKRAWPARTDESC paPartDescs = NULL;
4742 rc = vmdkRawDescAppendPartDesc(pImage, pRawDesc, (uint32_t)cLocations, &paPartDescs);
4743 AssertRCReturn(rc, rc);
4744
4745 /* Allocate the result table and repeat the location table query: */
4746 PRTDVMTABLELOCATION paLocations = (PRTDVMTABLELOCATION)RTMemAllocZ(sizeof(paLocations[0]) * cLocations);
4747 if (!paLocations)
4748 return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS, N_("VMDK: Image path: '%s'. Failed to allocate %zu bytes"),
4749 pImage->pszFilename, sizeof(paLocations[0]) * cLocations);
4750 rc = RTDvmMapQueryTableLocations(hVolMgr, RTDVMMAPQTABLOC_F_INCLUDE_LEGACY, paLocations, cLocations, NULL);
4751 if (RT_SUCCESS(rc))
4752 {
4753 /*
4754 * Translate them into descriptors.
4755 *
4756 * We restrict the amount of partition alignment padding to 4MiB as more
4757 * will just be a waste of space. The use case for including the padding
4758 * are older boot loaders and boot manager (including one by a team member)
4759 * that put data and code in the 62 sectors between the MBR and the first
4760 * partition (total of 63). Later CHS was abandond and partition started
4761 * being aligned on power of two sector boundraries (typically 64KiB or
4762 * 1MiB depending on the media size).
4763 */
4764 for (size_t i = 0; i < cLocations && RT_SUCCESS(rc); i++)
4765 {
4766 Assert(paLocations[i].cb > 0);
4767 if (paLocations[i].cb <= _64M)
4768 {
4769 /* Create the partition descriptor entry: */
4770 //paPartDescs[i].pszRawDevice = NULL;
4771 //paPartDescs[i].offStartInDevice = 0;
4772 //paPartDescs[i].uFlags = 0;
4773 paPartDescs[i].offStartInVDisk = paLocations[i].off;
4774 paPartDescs[i].cbData = paLocations[i].cb;
4775 if (paPartDescs[i].cbData < _4M)
4776 paPartDescs[i].cbData = RT_MIN(paPartDescs[i].cbData + paLocations[i].cbPadding, _4M);
4777 paPartDescs[i].pvPartitionData = RTMemAllocZ((size_t)paPartDescs[i].cbData);
4778 if (paPartDescs[i].pvPartitionData)
4779 {
4780 /* Read the content from the drive: */
4781 rc = RTFileReadAt(hRawDrive, paPartDescs[i].offStartInVDisk, paPartDescs[i].pvPartitionData,
4782 (size_t)paPartDescs[i].cbData, NULL);
4783 if (RT_SUCCESS(rc))
4784 {
4785 /* Do we have custom boot sector code? */
4786 if (pvBootSector && cbBootSector && paPartDescs[i].offStartInVDisk == 0)
4787 {
4788 /* Note! Old code used to quietly drop the bootsector if it was considered too big.
4789 Instead we fail as we weren't able to do what the user requested us to do.
4790 Better if the user knows than starts questioning why the guest isn't
4791 booting as expected. */
4792 if (cbBootSector <= paPartDescs[i].cbData)
4793 memcpy(paPartDescs[i].pvPartitionData, pvBootSector, cbBootSector);
4794 else
4795 rc = vdIfError(pImage->pIfError, VERR_TOO_MUCH_DATA, RT_SRC_POS,
4796 N_("VMDK: Image path: '%s'. The custom boot sector is too big: %zu bytes, %RU64 bytes available"),
4797 pImage->pszFilename, cbBootSector, paPartDescs[i].cbData);
4798 }
4799 }
4800 else
4801 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4802 N_("VMDK: Image path: '%s'. Failed to read partition at off %RU64 length %zu from '%s' (%Rrc)"),
4803 pImage->pszFilename, paPartDescs[i].offStartInVDisk,
4804 (size_t)paPartDescs[i].cbData, pszRawDrive, rc);
4805 }
4806 else
4807 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4808 N_("VMDK: Image path: '%s'. Failed to allocate %zu bytes for copying the partition table at off %RU64"),
4809 pImage->pszFilename, (size_t)paPartDescs[i].cbData, paPartDescs[i].offStartInVDisk);
4810 }
4811 else
4812 rc = vdIfError(pImage->pIfError, VERR_TOO_MUCH_DATA, RT_SRC_POS,
4813 N_("VMDK: Image path: '%s'. Partition table #%u at offset %RU64 in '%s' is to big: %RU64 bytes"),
4814 pImage->pszFilename, i, paLocations[i].off, pszRawDrive, paLocations[i].cb);
4815 }
4816 }
4817 else
4818 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4819 N_("VMDK: Image path: '%s'. RTDvmMapQueryTableLocations failed on '%s' (%Rrc)"),
4820 pImage->pszFilename, pszRawDrive, rc);
4821 RTMemFree(paLocations);
4822 return rc;
4823}
4824
4825/**
4826 * Opens the volume manager for the raw drive when in selected-partition mode.
4827 *
4828 * @param pImage The VMDK image (for errors).
4829 * @param hRawDrive The raw drive handle.
4830 * @param pszRawDrive The raw drive device path (for errors).
4831 * @param cbSector The sector size.
4832 * @param phVolMgr Where to return the handle to the volume manager on
4833 * success.
4834 * @returns VBox status code, errors have been reported.
4835 * @internal
4836 */
4837static int vmdkRawDescOpenVolMgr(PVMDKIMAGE pImage, RTFILE hRawDrive, const char *pszRawDrive, uint32_t cbSector, PRTDVM phVolMgr)
4838{
4839 *phVolMgr = NIL_RTDVM;
4840
4841 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
4842 int rc = RTVfsFileFromRTFile(hRawDrive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, true /*fLeaveOpen*/, &hVfsFile);
4843 if (RT_FAILURE(rc))
4844 return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4845 N_("VMDK: Image path: '%s'. RTVfsFileFromRTFile failed for '%s' handle (%Rrc)"),
4846 pImage->pszFilename, pszRawDrive, rc);
4847
4848 RTDVM hVolMgr = NIL_RTDVM;
4849 rc = RTDvmCreate(&hVolMgr, hVfsFile, cbSector, 0 /*fFlags*/);
4850
4851 RTVfsFileRelease(hVfsFile);
4852
4853 if (RT_FAILURE(rc))
4854 return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4855 N_("VMDK: Image path: '%s'. Failed to create volume manager instance for '%s' (%Rrc)"),
4856 pImage->pszFilename, pszRawDrive, rc);
4857
4858 rc = RTDvmMapOpen(hVolMgr);
4859 if (RT_SUCCESS(rc))
4860 {
4861 *phVolMgr = hVolMgr;
4862 return VINF_SUCCESS;
4863 }
4864 RTDvmRelease(hVolMgr);
4865 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: Image path: '%s'. RTDvmMapOpen failed for '%s' (%Rrc)"),
4866 pImage->pszFilename, pszRawDrive, rc);
4867}
4868
4869/**
4870 * Opens the raw drive device and get the sizes for it.
4871 *
4872 * @param pImage The image (for error reporting).
4873 * @param pszRawDrive The device/whatever to open.
4874 * @param phRawDrive Where to return the file handle.
4875 * @param pcbRawDrive Where to return the size.
4876 * @param pcbSector Where to return the sector size.
4877 * @returns IPRT status code, errors have been reported.
4878 * @internal
4879 */
4880static int vmkdRawDescOpenDevice(PVMDKIMAGE pImage, const char *pszRawDrive,
4881 PRTFILE phRawDrive, uint64_t *pcbRawDrive, uint32_t *pcbSector)
4882{
4883 /*
4884 * Open the device for the raw drive.
4885 */
4886 RTFILE hRawDrive = NIL_RTFILE;
4887 int rc = RTFileOpen(&hRawDrive, pszRawDrive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
4888 if (RT_FAILURE(rc))
4889 return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4890 N_("VMDK: Image path: '%s'. Failed to open the raw drive '%s' for reading (%Rrc)"),
4891 pImage->pszFilename, pszRawDrive, rc);
4892
4893 /*
4894 * Get the sector size.
4895 */
4896 uint32_t cbSector = 0;
4897 rc = RTFileQuerySectorSize(hRawDrive, &cbSector);
4898 if (RT_SUCCESS(rc))
4899 {
4900 /* sanity checks */
4901 if ( cbSector >= 512
4902 && cbSector <= _64K
4903 && RT_IS_POWER_OF_TWO(cbSector))
4904 {
4905 /*
4906 * Get the size.
4907 */
4908 uint64_t cbRawDrive = 0;
4909 rc = RTFileQuerySize(hRawDrive, &cbRawDrive);
4910 if (RT_SUCCESS(rc))
4911 {
4912 /* Check whether cbSize is actually sensible. */
4913 if (cbRawDrive > cbSector && (cbRawDrive % cbSector) == 0)
4914 {
4915 *phRawDrive = hRawDrive;
4916 *pcbRawDrive = cbRawDrive;
4917 *pcbSector = cbSector;
4918 return VINF_SUCCESS;
4919 }
4920 rc = vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
4921 N_("VMDK: Image path: '%s'. Got a bogus size for the raw drive '%s': %RU64 (sector size %u)"),
4922 pImage->pszFilename, pszRawDrive, cbRawDrive, cbSector);
4923 }
4924 else
4925 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4926 N_("VMDK: Image path: '%s'. Failed to query size of the drive '%s' (%Rrc)"),
4927 pImage->pszFilename, pszRawDrive, rc);
4928 }
4929 else
4930 rc = vdIfError(pImage->pIfError, VERR_OUT_OF_RANGE, RT_SRC_POS,
4931 N_("VMDK: Image path: '%s'. Unsupported sector size for '%s': %u (%#x)"),
4932 pImage->pszFilename, pszRawDrive, cbSector, cbSector);
4933 }
4934 else
4935 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4936 N_("VMDK: Image path: '%s'. Failed to get the sector size for '%s' (%Rrc)"),
4937 pImage->pszFilename, pszRawDrive, rc);
4938 RTFileClose(hRawDrive);
4939 return rc;
4940}
4941
4942/**
4943 * Reads the raw disk configuration, leaving initalization and cleanup to the
4944 * caller (regardless of return status).
4945 *
4946 * @returns VBox status code, errors properly reported.
4947 * @internal
4948 */
4949static int vmdkRawDescParseConfig(PVMDKIMAGE pImage, char **ppszRawDrive,
4950 uint32_t *pfPartitions, uint32_t *pfPartitionsReadOnly,
4951 void **ppvBootSector, size_t *pcbBootSector, bool *pfRelative,
4952 char **ppszFreeMe)
4953{
4954 PVDINTERFACECONFIG pImgCfg = VDIfConfigGet(pImage->pVDIfsImage);
4955 if (!pImgCfg)
4956 return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
4957 N_("VMDK: Image path: '%s'. Getting config interface failed"), pImage->pszFilename);
4958
4959 /*
4960 * RawDrive = path
4961 */
4962 int rc = VDCFGQueryStringAlloc(pImgCfg, "RawDrive", ppszRawDrive);
4963 if (RT_FAILURE(rc))
4964 return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
4965 N_("VMDK: Image path: '%s'. Getting 'RawDrive' configuration failed (%Rrc)"), pImage->pszFilename, rc);
4966 AssertPtrReturn(*ppszRawDrive, VERR_INTERNAL_ERROR_3);
4967
4968 /*
4969 * Partitions=n[r][,...]
4970 */
4971 uint32_t const cMaxPartitionBits = sizeof(*pfPartitions) * 8 /* ASSUMES 8 bits per char */;
4972 *pfPartitions = *pfPartitionsReadOnly = 0;
4973
4974 rc = VDCFGQueryStringAlloc(pImgCfg, "Partitions", ppszFreeMe);
4975 if (RT_SUCCESS(rc))
4976 {
4977 char *psz = *ppszFreeMe;
4978 while (*psz != '\0')
4979 {
4980 char *pszNext;
4981 uint32_t u32;
4982 rc = RTStrToUInt32Ex(psz, &pszNext, 0, &u32);
4983 if (rc == VWRN_NUMBER_TOO_BIG || rc == VWRN_NEGATIVE_UNSIGNED)
4984 rc = -rc;
4985 if (RT_FAILURE(rc))
4986 return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
4987 N_("VMDK: Image path: '%s'. Parsing 'Partitions' config value failed. Incorrect value (%Rrc): %s"),
4988 pImage->pszFilename, rc, psz);
4989 if (u32 >= cMaxPartitionBits)
4990 return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
4991 N_("VMDK: Image path: '%s'. 'Partitions' config sub-value out of range: %RU32, max %RU32"),
4992 pImage->pszFilename, u32, cMaxPartitionBits);
4993 *pfPartitions |= RT_BIT_32(u32);
4994 psz = pszNext;
4995 if (*psz == 'r')
4996 {
4997 *pfPartitionsReadOnly |= RT_BIT_32(u32);
4998 psz++;
4999 }
5000 if (*psz == ',')
5001 psz++;
5002 else if (*psz != '\0')
5003 return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
5004 N_("VMDK: Image path: '%s'. Malformed 'Partitions' config value, expected separator: %s"),
5005 pImage->pszFilename, psz);
5006 }
5007
5008 RTStrFree(*ppszFreeMe);
5009 *ppszFreeMe = NULL;
5010 }
5011 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
5012 return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
5013 N_("VMDK: Image path: '%s'. Getting 'Partitions' configuration failed (%Rrc)"), pImage->pszFilename, rc);
5014
5015 /*
5016 * BootSector=base64
5017 */
5018 rc = VDCFGQueryStringAlloc(pImgCfg, "BootSector", ppszFreeMe);
5019 if (RT_SUCCESS(rc))
5020 {
5021 ssize_t cbBootSector = RTBase64DecodedSize(*ppszFreeMe, NULL);
5022 if (cbBootSector < 0)
5023 return vdIfError(pImage->pIfError, VERR_INVALID_BASE64_ENCODING, RT_SRC_POS,
5024 N_("VMDK: Image path: '%s'. BASE64 decoding failed on the custom bootsector for '%s'"),
5025 pImage->pszFilename, *ppszRawDrive);
5026 if (cbBootSector == 0)
5027 return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
5028 N_("VMDK: Image path: '%s'. Custom bootsector for '%s' is zero bytes big"),
5029 pImage->pszFilename, *ppszRawDrive);
5030 if (cbBootSector > _4M) /* this is just a preliminary max */
5031 return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
5032 N_("VMDK: Image path: '%s'. Custom bootsector for '%s' is way too big: %zu bytes, max 4MB"),
5033 pImage->pszFilename, *ppszRawDrive, cbBootSector);
5034
5035 /* Refuse the boot sector if whole-drive. This used to be done quietly,
5036 however, bird disagrees and thinks the user should be told that what
5037 he/she/it tries to do isn't possible. There should be less head
5038 scratching this way when the guest doesn't do the expected thing. */
5039 if (!*pfPartitions)
5040 return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
5041 N_("VMDK: Image path: '%s'. Custom bootsector for '%s' is not supported for whole-drive configurations, only when selecting partitions"),
5042 pImage->pszFilename, *ppszRawDrive);
5043
5044 *pcbBootSector = (size_t)cbBootSector;
5045 *ppvBootSector = RTMemAlloc((size_t)cbBootSector);
5046 if (!*ppvBootSector)
5047 return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
5048 N_("VMDK: Image path: '%s'. Failed to allocate %zd bytes for the custom bootsector for '%s'"),
5049 pImage->pszFilename, cbBootSector, *ppszRawDrive);
5050
5051 rc = RTBase64Decode(*ppszFreeMe, *ppvBootSector, cbBootSector, NULL /*pcbActual*/, NULL /*ppszEnd*/);
5052 if (RT_FAILURE(rc))
5053 return vdIfError(pImage->pIfError, VERR_NO_MEMORY, RT_SRC_POS,
5054 N_("VMDK: Image path: '%s'. Base64 decoding of the custom boot sector for '%s' failed (%Rrc)"),
5055 pImage->pszFilename, *ppszRawDrive, rc);
5056
5057 RTStrFree(*ppszFreeMe);
5058 *ppszFreeMe = NULL;
5059 }
5060 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
5061 return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
5062 N_("VMDK: Image path: '%s'. Getting 'BootSector' configuration failed (%Rrc)"), pImage->pszFilename, rc);
5063
5064 /*
5065 * Relative=0/1
5066 */
5067 *pfRelative = false;
5068 rc = VDCFGQueryBool(pImgCfg, "Relative", pfRelative);
5069 if (RT_SUCCESS(rc))
5070 {
5071 if (!*pfPartitions && *pfRelative != false)
5072 return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
5073 N_("VMDK: Image path: '%s'. The 'Relative' option is not supported for whole-drive configurations, only when selecting partitions"),
5074 pImage->pszFilename);
5075#if !defined(RT_OS_DARWIN) && !defined(RT_OS_LINUX) && !defined(RT_OS_FREEBSD) && !defined(RT_OS_WINDOWS) && !defined(RT_OS_SOLARIS) /* PORTME */
5076 if (*pfRelative == true)
5077 return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
5078 N_("VMDK: Image path: '%s'. The 'Relative' option is not supported on this host OS"),
5079 pImage->pszFilename);
5080#endif
5081 }
5082 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
5083 return vdIfError(pImage->pIfError, rc, RT_SRC_POS,
5084 N_("VMDK: Image path: '%s'. Getting 'Relative' configuration failed (%Rrc)"), pImage->pszFilename, rc);
5085 else
5086#ifdef RT_OS_DARWIN /* different default on macOS, see ticketref:1461 (comment 20). */
5087 *pfRelative = true;
5088#else
5089 *pfRelative = false;
5090#endif
5091
5092 return VINF_SUCCESS;
5093}
5094
5095/**
5096 * Creates a raw drive (nee disk) descriptor.
5097 *
5098 * This was originally done in VBoxInternalManage.cpp, but was copied (not move)
5099 * here much later. That's one of the reasons why we produce a descriptor just
5100 * like it does, rather than mixing directly into the vmdkCreateRawImage code.
5101 *
5102 * @returns VBox status code.
5103 * @param pImage The image.
5104 * @param ppRaw Where to return the raw drive descriptor. Caller must
5105 * free it using vmdkRawDescFree regardless of the status
5106 * code.
5107 * @internal
5108 */
5109static int vmdkMakeRawDescriptor(PVMDKIMAGE pImage, PVDISKRAW *ppRaw)
5110{
5111 /* Make sure it's NULL. */
5112 *ppRaw = NULL;
5113
5114 /*
5115 * Read the configuration.
5116 */
5117 char *pszRawDrive = NULL;
5118 uint32_t fPartitions = 0; /* zero if whole-drive */
5119 uint32_t fPartitionsReadOnly = 0; /* (subset of fPartitions) */
5120 void *pvBootSector = NULL;
5121 size_t cbBootSector = 0;
5122 bool fRelative = false;
5123 char *pszFreeMe = NULL; /* lazy bird cleanup. */
5124 int rc = vmdkRawDescParseConfig(pImage, &pszRawDrive, &fPartitions, &fPartitionsReadOnly,
5125 &pvBootSector, &cbBootSector, &fRelative, &pszFreeMe);
5126 RTStrFree(pszFreeMe);
5127 if (RT_SUCCESS(rc))
5128 {
5129 /*
5130 * Open the device, getting the sector size and drive size.
5131 */
5132 uint64_t cbSize = 0;
5133 uint32_t cbSector = 0;
5134 RTFILE hRawDrive = NIL_RTFILE;
5135 rc = vmkdRawDescOpenDevice(pImage, pszRawDrive, &hRawDrive, &cbSize, &cbSector);
5136 if (RT_SUCCESS(rc))
5137 {
5138 pImage->cbSize = cbSize;
5139 /*
5140 * Create the raw-drive descriptor
5141 */
5142 PVDISKRAW pRawDesc = (PVDISKRAW)RTMemAllocZ(sizeof(*pRawDesc));
5143 if (pRawDesc)
5144 {
5145 pRawDesc->szSignature[0] = 'R';
5146 pRawDesc->szSignature[1] = 'A';
5147 pRawDesc->szSignature[2] = 'W';
5148 //pRawDesc->szSignature[3] = '\0';
5149 if (!fPartitions)
5150 {
5151 /*
5152 * It's simple for when doing the whole drive.
5153 */
5154 pRawDesc->uFlags = VDISKRAW_DISK;
5155 rc = RTStrDupEx(&pRawDesc->pszRawDisk, pszRawDrive);
5156 }
5157 else
5158 {
5159 /*
5160 * In selected partitions mode we've got a lot more work ahead of us.
5161 */
5162 pRawDesc->uFlags = VDISKRAW_NORMAL;
5163 //pRawDesc->pszRawDisk = NULL;
5164 //pRawDesc->cPartDescs = 0;
5165 //pRawDesc->pPartDescs = NULL;
5166
5167 /* We need to parse the partition map to complete the descriptor: */
5168 RTDVM hVolMgr = NIL_RTDVM;
5169 rc = vmdkRawDescOpenVolMgr(pImage, hRawDrive, pszRawDrive, cbSector, &hVolMgr);
5170 if (RT_SUCCESS(rc))
5171 {
5172 RTDVMFORMATTYPE enmFormatType = RTDvmMapGetFormatType(hVolMgr);
5173 if ( enmFormatType == RTDVMFORMATTYPE_MBR
5174 || enmFormatType == RTDVMFORMATTYPE_GPT)
5175 {
5176 pRawDesc->enmPartitioningType = enmFormatType == RTDVMFORMATTYPE_MBR
5177 ? VDISKPARTTYPE_MBR : VDISKPARTTYPE_GPT;
5178
5179 /* Add copies of the partition tables: */
5180 rc = vmdkRawDescDoCopyPartitionTables(pImage, hVolMgr, pRawDesc, pszRawDrive, hRawDrive,
5181 pvBootSector, cbBootSector);
5182 if (RT_SUCCESS(rc))
5183 {
5184 /* Add descriptors for the partitions/volumes, indicating which
5185 should be accessible and how to access them: */
5186 RTDVMVOLUME hVolRelease = NIL_RTDVMVOLUME;
5187 rc = vmdkRawDescDoPartitions(pImage, hVolMgr, pRawDesc, hRawDrive, pszRawDrive, cbSector,
5188 fPartitions, fPartitionsReadOnly, fRelative, &hVolRelease);
5189 RTDvmVolumeRelease(hVolRelease);
5190
5191 /* Finally, sort the partition and check consistency (overlaps, etc): */
5192 if (RT_SUCCESS(rc))
5193 rc = vmdkRawDescPostProcessPartitions(pImage, pRawDesc, cbSize);
5194 }
5195 }
5196 else
5197 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
5198 N_("VMDK: Image path: '%s'. Unsupported partitioning for the disk '%s': %s"),
5199 pImage->pszFilename, pszRawDrive, RTDvmMapGetFormatType(hVolMgr));
5200 RTDvmRelease(hVolMgr);
5201 }
5202 }
5203 if (RT_SUCCESS(rc))
5204 {
5205 /*
5206 * We succeeded.
5207 */
5208 *ppRaw = pRawDesc;
5209 Log(("vmdkMakeRawDescriptor: fFlags=%#x enmPartitioningType=%d cPartDescs=%u pszRawDisk=%s\n",
5210 pRawDesc->uFlags, pRawDesc->enmPartitioningType, pRawDesc->cPartDescs, pRawDesc->pszRawDisk));
5211 if (pRawDesc->cPartDescs)
5212 {
5213 Log(("# VMDK offset Length Device offset PartDataPtr Device\n"));
5214 for (uint32_t i = 0; i < pRawDesc->cPartDescs; i++)
5215 Log(("%2u %14RU64 %14RU64 %14RU64 %#18p %s\n", i, pRawDesc->pPartDescs[i].offStartInVDisk,
5216 pRawDesc->pPartDescs[i].cbData, pRawDesc->pPartDescs[i].offStartInDevice,
5217 pRawDesc->pPartDescs[i].pvPartitionData, pRawDesc->pPartDescs[i].pszRawDevice));
5218 }
5219 }
5220 else
5221 vmdkRawDescFree(pRawDesc);
5222 }
5223 else
5224 rc = vdIfError(pImage->pIfError, VERR_NOT_SUPPORTED, RT_SRC_POS,
5225 N_("VMDK: Image path: '%s'. Failed to allocate %u bytes for the raw drive descriptor"),
5226 pImage->pszFilename, sizeof(*pRawDesc));
5227 RTFileClose(hRawDrive);
5228 }
5229 }
5230 RTStrFree(pszRawDrive);
5231 RTMemFree(pvBootSector);
5232 return rc;
5233}
5234
5235/**
5236 * Internal: create VMDK images for raw disk/partition access.
5237 */
5238static int vmdkCreateRawImage(PVMDKIMAGE pImage, const PVDISKRAW pRaw,
5239 uint64_t cbSize)
5240{
5241 int rc = VINF_SUCCESS;
5242 PVMDKEXTENT pExtent;
5243
5244 if (pRaw->uFlags & VDISKRAW_DISK)
5245 {
5246 /* Full raw disk access. This requires setting up a descriptor
5247 * file and open the (flat) raw disk. */
5248 rc = vmdkCreateExtents(pImage, 1);
5249 if (RT_FAILURE(rc))
5250 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
5251 pExtent = &pImage->pExtents[0];
5252 /* Create raw disk descriptor file. */
5253 rc = vmdkFileOpen(pImage, &pImage->pFile, NULL, pImage->pszFilename,
5254 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
5255 true /* fCreate */));
5256 if (RT_FAILURE(rc))
5257 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pImage->pszFilename);
5258
5259 /* Set up basename for extent description. Cannot use StrDup. */
5260 size_t cbBasename = strlen(pRaw->pszRawDisk) + 1;
5261 char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
5262 if (!pszBasename)
5263 return VERR_NO_MEMORY;
5264 memcpy(pszBasename, pRaw->pszRawDisk, cbBasename);
5265 pExtent->pszBasename = pszBasename;
5266 /* For raw disks the full name is identical to the base name. */
5267 pExtent->pszFullname = RTStrDup(pszBasename);
5268 if (!pExtent->pszFullname)
5269 return VERR_NO_MEMORY;
5270 pExtent->enmType = VMDKETYPE_FLAT;
5271 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize);
5272 pExtent->uSectorOffset = 0;
5273 pExtent->enmAccess = (pRaw->uFlags & VDISKRAW_READONLY) ? VMDKACCESS_READONLY : VMDKACCESS_READWRITE;
5274 pExtent->fMetaDirty = false;
5275
5276 /* Open flat image, the raw disk. */
5277 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszBasename, pExtent->pszFullname,
5278 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags | ((pExtent->enmAccess == VMDKACCESS_READONLY) ? VD_OPEN_FLAGS_READONLY : 0),
5279 false /* fCreate */));
5280 if (RT_FAILURE(rc))
5281 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not open raw disk file '%s'"), pExtent->pszFullname);
5282 }
5283 else
5284 {
5285 /* Raw partition access. This requires setting up a descriptor
5286 * file, write the partition information to a flat extent and
5287 * open all the (flat) raw disk partitions. */
5288
5289 /* First pass over the partition data areas to determine how many
5290 * extents we need. One data area can require up to 2 extents, as
5291 * it might be necessary to skip over unpartitioned space. */
5292 unsigned cExtents = 0;
5293 uint64_t uStart = 0;
5294 for (unsigned i = 0; i < pRaw->cPartDescs; i++)
5295 {
5296 PVDISKRAWPARTDESC pPart = &pRaw->pPartDescs[i];
5297 if (uStart > pPart->offStartInVDisk)
5298 return vdIfError(pImage->pIfError, VERR_INVALID_PARAMETER, RT_SRC_POS,
5299 N_("VMDK: incorrect partition data area ordering set up by the caller in '%s'"), pImage->pszFilename);
5300
5301 if (uStart < pPart->offStartInVDisk)
5302 cExtents++;
5303 uStart = pPart->offStartInVDisk + pPart->cbData;
5304 cExtents++;
5305 }
5306 /* Another extent for filling up the rest of the image. */
5307 if (uStart != cbSize)
5308 cExtents++;
5309
5310 rc = vmdkCreateExtents(pImage, cExtents);
5311 if (RT_FAILURE(rc))
5312 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
5313
5314 /* Create raw partition descriptor file. */
5315 rc = vmdkFileOpen(pImage, &pImage->pFile, NULL, pImage->pszFilename,
5316 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
5317 true /* fCreate */));
5318 if (RT_FAILURE(rc))
5319 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pImage->pszFilename);
5320
5321 /* Create base filename for the partition table extent. */
5322 /** @todo remove fixed buffer without creating memory leaks. */
5323 char pszPartition[1024];
5324 const char *pszBase = RTPathFilename(pImage->pszFilename);
5325 const char *pszSuff = RTPathSuffix(pszBase);
5326 if (pszSuff == NULL)
5327 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: invalid filename '%s'"), pImage->pszFilename);
5328 char *pszBaseBase = RTStrDup(pszBase);
5329 if (!pszBaseBase)
5330 return VERR_NO_MEMORY;
5331 RTPathStripSuffix(pszBaseBase);
5332 RTStrPrintf(pszPartition, sizeof(pszPartition), "%s-pt%s",
5333 pszBaseBase, pszSuff);
5334 RTStrFree(pszBaseBase);
5335
5336 /* Second pass over the partitions, now define all extents. */
5337 uint64_t uPartOffset = 0;
5338 cExtents = 0;
5339 uStart = 0;
5340 for (unsigned i = 0; i < pRaw->cPartDescs; i++)
5341 {
5342 PVDISKRAWPARTDESC pPart = &pRaw->pPartDescs[i];
5343 pExtent = &pImage->pExtents[cExtents++];
5344
5345 if (uStart < pPart->offStartInVDisk)
5346 {
5347 pExtent->pszBasename = NULL;
5348 pExtent->pszFullname = NULL;
5349 pExtent->enmType = VMDKETYPE_ZERO;
5350 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->offStartInVDisk - uStart);
5351 pExtent->uSectorOffset = 0;
5352 pExtent->enmAccess = VMDKACCESS_READWRITE;
5353 pExtent->fMetaDirty = false;
5354 /* go to next extent */
5355 pExtent = &pImage->pExtents[cExtents++];
5356 }
5357 uStart = pPart->offStartInVDisk + pPart->cbData;
5358
5359 if (pPart->pvPartitionData)
5360 {
5361 /* Set up basename for extent description. Can't use StrDup. */
5362 size_t cbBasename = strlen(pszPartition) + 1;
5363 char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
5364 if (!pszBasename)
5365 return VERR_NO_MEMORY;
5366 memcpy(pszBasename, pszPartition, cbBasename);
5367 pExtent->pszBasename = pszBasename;
5368
5369 /* Set up full name for partition extent. */
5370 char *pszDirname = RTStrDup(pImage->pszFilename);
5371 if (!pszDirname)
5372 return VERR_NO_STR_MEMORY;
5373 RTPathStripFilename(pszDirname);
5374 char *pszFullname = RTPathJoinA(pszDirname, pExtent->pszBasename);
5375 RTStrFree(pszDirname);
5376 if (!pszFullname)
5377 return VERR_NO_STR_MEMORY;
5378 pExtent->pszFullname = pszFullname;
5379 pExtent->enmType = VMDKETYPE_FLAT;
5380 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbData);
5381 pExtent->uSectorOffset = uPartOffset;
5382 pExtent->enmAccess = VMDKACCESS_READWRITE;
5383 pExtent->fMetaDirty = false;
5384
5385 /* Create partition table flat image. */
5386 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszBasename, pExtent->pszFullname,
5387 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags | ((pExtent->enmAccess == VMDKACCESS_READONLY) ? VD_OPEN_FLAGS_READONLY : 0),
5388 true /* fCreate */));
5389 if (RT_FAILURE(rc))
5390 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new partition data file '%s'"), pExtent->pszFullname);
5391 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
5392 VMDK_SECTOR2BYTE(uPartOffset),
5393 pPart->pvPartitionData,
5394 pPart->cbData);
5395 if (RT_FAILURE(rc))
5396 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not write partition data to '%s'"), pExtent->pszFullname);
5397 uPartOffset += VMDK_BYTE2SECTOR(pPart->cbData);
5398 }
5399 else
5400 {
5401 if (pPart->pszRawDevice)
5402 {
5403 /* Set up basename for extent descr. Can't use StrDup. */
5404 size_t cbBasename = strlen(pPart->pszRawDevice) + 1;
5405 char *pszBasename = (char *)RTMemTmpAlloc(cbBasename);
5406 if (!pszBasename)
5407 return VERR_NO_MEMORY;
5408 memcpy(pszBasename, pPart->pszRawDevice, cbBasename);
5409 pExtent->pszBasename = pszBasename;
5410 /* For raw disks full name is identical to base name. */
5411 pExtent->pszFullname = RTStrDup(pszBasename);
5412 if (!pExtent->pszFullname)
5413 return VERR_NO_MEMORY;
5414 pExtent->enmType = VMDKETYPE_FLAT;
5415 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbData);
5416 pExtent->uSectorOffset = VMDK_BYTE2SECTOR(pPart->offStartInDevice);
5417 pExtent->enmAccess = (pPart->uFlags & VDISKRAW_READONLY) ? VMDKACCESS_READONLY : VMDKACCESS_READWRITE;
5418 pExtent->fMetaDirty = false;
5419
5420 /* Open flat image, the raw partition. */
5421 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszBasename, pExtent->pszFullname,
5422 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags | ((pExtent->enmAccess == VMDKACCESS_READONLY) ? VD_OPEN_FLAGS_READONLY : 0),
5423 false /* fCreate */));
5424 if (RT_FAILURE(rc))
5425 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not open raw partition file '%s'"), pExtent->pszFullname);
5426 }
5427 else
5428 {
5429 pExtent->pszBasename = NULL;
5430 pExtent->pszFullname = NULL;
5431 pExtent->enmType = VMDKETYPE_ZERO;
5432 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(pPart->cbData);
5433 pExtent->uSectorOffset = 0;
5434 pExtent->enmAccess = VMDKACCESS_READWRITE;
5435 pExtent->fMetaDirty = false;
5436 }
5437 }
5438 }
5439 /* Another extent for filling up the rest of the image. */
5440 if (uStart != cbSize)
5441 {
5442 pExtent = &pImage->pExtents[cExtents++];
5443 pExtent->pszBasename = NULL;
5444 pExtent->pszFullname = NULL;
5445 pExtent->enmType = VMDKETYPE_ZERO;
5446 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize - uStart);
5447 pExtent->uSectorOffset = 0;
5448 pExtent->enmAccess = VMDKACCESS_READWRITE;
5449 pExtent->fMetaDirty = false;
5450 }
5451 }
5452
5453 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType",
5454 (pRaw->uFlags & VDISKRAW_DISK) ?
5455 "fullDevice" : "partitionedDevice");
5456 if (RT_FAILURE(rc))
5457 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename);
5458 return rc;
5459}
5460
5461/**
5462 * Internal: create a regular (i.e. file-backed) VMDK image.
5463 */
5464static int vmdkCreateRegularImage(PVMDKIMAGE pImage, uint64_t cbSize,
5465 unsigned uImageFlags, PVDINTERFACEPROGRESS pIfProgress,
5466 unsigned uPercentStart, unsigned uPercentSpan)
5467{
5468 int rc = VINF_SUCCESS;
5469 unsigned cExtents = 1;
5470 uint64_t cbOffset = 0;
5471 uint64_t cbRemaining = cbSize;
5472
5473 if (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
5474 {
5475 cExtents = cbSize / VMDK_2G_SPLIT_SIZE;
5476 /* Do proper extent computation: need one smaller extent if the total
5477 * size isn't evenly divisible by the split size. */
5478 if (cbSize % VMDK_2G_SPLIT_SIZE)
5479 cExtents++;
5480 }
5481 rc = vmdkCreateExtents(pImage, cExtents);
5482 if (RT_FAILURE(rc))
5483 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
5484
5485 /* Basename strings needed for constructing the extent names. */
5486 char *pszBasenameSubstr = RTPathFilename(pImage->pszFilename);
5487 AssertPtr(pszBasenameSubstr);
5488 size_t cbBasenameSubstr = strlen(pszBasenameSubstr) + 1;
5489
5490 /* Create separate descriptor file if necessary. */
5491 if (cExtents != 1 || (uImageFlags & VD_IMAGE_FLAGS_FIXED))
5492 {
5493 rc = vmdkFileOpen(pImage, &pImage->pFile, NULL, pImage->pszFilename,
5494 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
5495 true /* fCreate */));
5496 if (RT_FAILURE(rc))
5497 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new sparse descriptor file '%s'"), pImage->pszFilename);
5498 }
5499 else
5500 pImage->pFile = NULL;
5501
5502 /* Set up all extents. */
5503 for (unsigned i = 0; i < cExtents; i++)
5504 {
5505 PVMDKEXTENT pExtent = &pImage->pExtents[i];
5506 uint64_t cbExtent = cbRemaining;
5507
5508 /* Set up fullname/basename for extent description. Cannot use StrDup
5509 * for basename, as it is not guaranteed that the memory can be freed
5510 * with RTMemTmpFree, which must be used as in other code paths
5511 * StrDup is not usable. */
5512 if (cExtents == 1 && !(uImageFlags & VD_IMAGE_FLAGS_FIXED))
5513 {
5514 char *pszBasename = (char *)RTMemTmpAlloc(cbBasenameSubstr);
5515 if (!pszBasename)
5516 return VERR_NO_MEMORY;
5517 memcpy(pszBasename, pszBasenameSubstr, cbBasenameSubstr);
5518 pExtent->pszBasename = pszBasename;
5519 }
5520 else
5521 {
5522 char *pszBasenameSuff = RTPathSuffix(pszBasenameSubstr);
5523 char *pszBasenameBase = RTStrDup(pszBasenameSubstr);
5524 RTPathStripSuffix(pszBasenameBase);
5525 char *pszTmp;
5526 size_t cbTmp;
5527 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
5528 {
5529 if (cExtents == 1)
5530 RTStrAPrintf(&pszTmp, "%s-flat%s", pszBasenameBase,
5531 pszBasenameSuff);
5532 else
5533 RTStrAPrintf(&pszTmp, "%s-f%03d%s", pszBasenameBase,
5534 i+1, pszBasenameSuff);
5535 }
5536 else
5537 RTStrAPrintf(&pszTmp, "%s-s%03d%s", pszBasenameBase, i+1,
5538 pszBasenameSuff);
5539 RTStrFree(pszBasenameBase);
5540 if (!pszTmp)
5541 return VERR_NO_STR_MEMORY;
5542 cbTmp = strlen(pszTmp) + 1;
5543 char *pszBasename = (char *)RTMemTmpAlloc(cbTmp);
5544 if (!pszBasename)
5545 {
5546 RTStrFree(pszTmp);
5547 return VERR_NO_MEMORY;
5548 }
5549 memcpy(pszBasename, pszTmp, cbTmp);
5550 RTStrFree(pszTmp);
5551 pExtent->pszBasename = pszBasename;
5552 if (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
5553 cbExtent = RT_MIN(cbRemaining, VMDK_2G_SPLIT_SIZE);
5554 }
5555 char *pszBasedirectory = RTStrDup(pImage->pszFilename);
5556 if (!pszBasedirectory)
5557 return VERR_NO_STR_MEMORY;
5558 RTPathStripFilename(pszBasedirectory);
5559 char *pszFullname = RTPathJoinA(pszBasedirectory, pExtent->pszBasename);
5560 RTStrFree(pszBasedirectory);
5561 if (!pszFullname)
5562 return VERR_NO_STR_MEMORY;
5563 pExtent->pszFullname = pszFullname;
5564
5565 /* Create file for extent. */
5566 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszBasename, pExtent->pszFullname,
5567 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
5568 true /* fCreate */));
5569 if (RT_FAILURE(rc))
5570 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname);
5571 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
5572 {
5573 rc = vdIfIoIntFileSetAllocationSize(pImage->pIfIo, pExtent->pFile->pStorage, cbExtent,
5574 0 /* fFlags */, pIfProgress,
5575 uPercentStart + cbOffset * uPercentSpan / cbSize,
5576 cbExtent * uPercentSpan / cbSize);
5577 if (RT_FAILURE(rc))
5578 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set size of new file '%s'"), pExtent->pszFullname);
5579 }
5580
5581 /* Place descriptor file information (where integrated). */
5582 if (cExtents == 1 && !(uImageFlags & VD_IMAGE_FLAGS_FIXED))
5583 {
5584 pExtent->uDescriptorSector = 1;
5585 pExtent->cDescriptorSectors = VMDK_BYTE2SECTOR(pImage->cbDescAlloc);
5586 /* The descriptor is part of the (only) extent. */
5587 pExtent->pDescData = pImage->pDescData;
5588 pImage->pDescData = NULL;
5589 }
5590
5591 if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
5592 {
5593 uint64_t cSectorsPerGDE, cSectorsPerGD;
5594 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;
5595 pExtent->cSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(cbExtent, _64K));
5596 pExtent->cSectorsPerGrain = VMDK_BYTE2SECTOR(_64K);
5597 pExtent->cGTEntries = 512;
5598 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
5599 pExtent->cSectorsPerGDE = cSectorsPerGDE;
5600 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
5601 cSectorsPerGD = (pExtent->cGDEntries + (512 / sizeof(uint32_t) - 1)) / (512 / sizeof(uint32_t));
5602 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
5603 {
5604 /* The spec says version is 1 for all VMDKs, but the vast
5605 * majority of streamOptimized VMDKs actually contain
5606 * version 3 - so go with the majority. Both are accepted. */
5607 pExtent->uVersion = 3;
5608 pExtent->uCompression = VMDK_COMPRESSION_DEFLATE;
5609 }
5610 }
5611 else
5612 {
5613 if (uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX)
5614 pExtent->enmType = VMDKETYPE_VMFS;
5615 else
5616 pExtent->enmType = VMDKETYPE_FLAT;
5617 }
5618
5619 pExtent->enmAccess = VMDKACCESS_READWRITE;
5620 pExtent->fUncleanShutdown = true;
5621 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbExtent);
5622 pExtent->uSectorOffset = 0;
5623 pExtent->fMetaDirty = true;
5624
5625 if (!(uImageFlags & VD_IMAGE_FLAGS_FIXED))
5626 {
5627 /* fPreAlloc should never be false because VMware can't use such images. */
5628 rc = vmdkCreateGrainDirectory(pImage, pExtent,
5629 RT_MAX( pExtent->uDescriptorSector
5630 + pExtent->cDescriptorSectors,
5631 1),
5632 true /* fPreAlloc */);
5633 if (RT_FAILURE(rc))
5634 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new grain directory in '%s'"), pExtent->pszFullname);
5635 }
5636
5637 cbOffset += cbExtent;
5638
5639 if (RT_SUCCESS(rc))
5640 vdIfProgress(pIfProgress, uPercentStart + cbOffset * uPercentSpan / cbSize);
5641
5642 cbRemaining -= cbExtent;
5643 }
5644
5645 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX)
5646 {
5647 /* VirtualBox doesn't care, but VMWare ESX freaks out if the wrong
5648 * controller type is set in an image. */
5649 rc = vmdkDescDDBSetStr(pImage, &pImage->Descriptor, "ddb.adapterType", "lsilogic");
5650 if (RT_FAILURE(rc))
5651 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set controller type to lsilogic in '%s'"), pImage->pszFilename);
5652 }
5653
5654 const char *pszDescType = NULL;
5655 if (uImageFlags & VD_IMAGE_FLAGS_FIXED)
5656 {
5657 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX)
5658 pszDescType = "vmfs";
5659 else
5660 pszDescType = (cExtents == 1)
5661 ? "monolithicFlat" : "twoGbMaxExtentFlat";
5662 }
5663 else
5664 {
5665 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
5666 pszDescType = "streamOptimized";
5667 else
5668 {
5669 pszDescType = (cExtents == 1)
5670 ? "monolithicSparse" : "twoGbMaxExtentSparse";
5671 }
5672 }
5673 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType",
5674 pszDescType);
5675 if (RT_FAILURE(rc))
5676 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename);
5677 return rc;
5678}
5679
5680/**
5681 * Internal: Create a real stream optimized VMDK using only linear writes.
5682 */
5683static int vmdkCreateStreamImage(PVMDKIMAGE pImage, uint64_t cbSize)
5684{
5685 int rc = vmdkCreateExtents(pImage, 1);
5686 if (RT_FAILURE(rc))
5687 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new extent list in '%s'"), pImage->pszFilename);
5688
5689 /* Basename strings needed for constructing the extent names. */
5690 const char *pszBasenameSubstr = RTPathFilename(pImage->pszFilename);
5691 AssertPtr(pszBasenameSubstr);
5692 size_t cbBasenameSubstr = strlen(pszBasenameSubstr) + 1;
5693
5694 /* No separate descriptor file. */
5695 pImage->pFile = NULL;
5696
5697 /* Set up all extents. */
5698 PVMDKEXTENT pExtent = &pImage->pExtents[0];
5699
5700 /* Set up fullname/basename for extent description. Cannot use StrDup
5701 * for basename, as it is not guaranteed that the memory can be freed
5702 * with RTMemTmpFree, which must be used as in other code paths
5703 * StrDup is not usable. */
5704 char *pszBasename = (char *)RTMemTmpAlloc(cbBasenameSubstr);
5705 if (!pszBasename)
5706 return VERR_NO_MEMORY;
5707 memcpy(pszBasename, pszBasenameSubstr, cbBasenameSubstr);
5708 pExtent->pszBasename = pszBasename;
5709
5710 char *pszBasedirectory = RTStrDup(pImage->pszFilename);
5711 RTPathStripFilename(pszBasedirectory);
5712 char *pszFullname = RTPathJoinA(pszBasedirectory, pExtent->pszBasename);
5713 RTStrFree(pszBasedirectory);
5714 if (!pszFullname)
5715 return VERR_NO_STR_MEMORY;
5716 pExtent->pszFullname = pszFullname;
5717
5718 /* Create file for extent. Make it write only, no reading allowed. */
5719 rc = vmdkFileOpen(pImage, &pExtent->pFile, pExtent->pszBasename, pExtent->pszFullname,
5720 VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags,
5721 true /* fCreate */)
5722 & ~RTFILE_O_READ);
5723 if (RT_FAILURE(rc))
5724 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new file '%s'"), pExtent->pszFullname);
5725
5726 /* Place descriptor file information. */
5727 pExtent->uDescriptorSector = 1;
5728 pExtent->cDescriptorSectors = VMDK_BYTE2SECTOR(pImage->cbDescAlloc);
5729 /* The descriptor is part of the (only) extent. */
5730 pExtent->pDescData = pImage->pDescData;
5731 pImage->pDescData = NULL;
5732
5733 uint64_t cSectorsPerGDE, cSectorsPerGD;
5734 pExtent->enmType = VMDKETYPE_HOSTED_SPARSE;
5735 pExtent->cSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64(cbSize, _64K));
5736 pExtent->cSectorsPerGrain = VMDK_BYTE2SECTOR(_64K);
5737 pExtent->cGTEntries = 512;
5738 cSectorsPerGDE = pExtent->cGTEntries * pExtent->cSectorsPerGrain;
5739 pExtent->cSectorsPerGDE = cSectorsPerGDE;
5740 pExtent->cGDEntries = (pExtent->cSectors + cSectorsPerGDE - 1) / cSectorsPerGDE;
5741 cSectorsPerGD = (pExtent->cGDEntries + (512 / sizeof(uint32_t) - 1)) / (512 / sizeof(uint32_t));
5742
5743 /* The spec says version is 1 for all VMDKs, but the vast
5744 * majority of streamOptimized VMDKs actually contain
5745 * version 3 - so go with the majority. Both are accepted. */
5746 pExtent->uVersion = 3;
5747 pExtent->uCompression = VMDK_COMPRESSION_DEFLATE;
5748 pExtent->fFooter = true;
5749
5750 pExtent->enmAccess = VMDKACCESS_READONLY;
5751 pExtent->fUncleanShutdown = false;
5752 pExtent->cNominalSectors = VMDK_BYTE2SECTOR(cbSize);
5753 pExtent->uSectorOffset = 0;
5754 pExtent->fMetaDirty = true;
5755
5756 /* Create grain directory, without preallocating it straight away. It will
5757 * be constructed on the fly when writing out the data and written when
5758 * closing the image. The end effect is that the full grain directory is
5759 * allocated, which is a requirement of the VMDK specs. */
5760 rc = vmdkCreateGrainDirectory(pImage, pExtent, VMDK_GD_AT_END,
5761 false /* fPreAlloc */);
5762 if (RT_FAILURE(rc))
5763 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new grain directory in '%s'"), pExtent->pszFullname);
5764
5765 rc = vmdkDescBaseSetStr(pImage, &pImage->Descriptor, "createType",
5766 "streamOptimized");
5767 if (RT_FAILURE(rc))
5768 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set the image type in '%s'"), pImage->pszFilename);
5769
5770 return rc;
5771}
5772
5773/**
5774 * Initializes the UUID fields in the DDB.
5775 *
5776 * @returns VBox status code.
5777 * @param pImage The VMDK image instance.
5778 */
5779static int vmdkCreateImageDdbUuidsInit(PVMDKIMAGE pImage)
5780{
5781 int rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, VMDK_DDB_IMAGE_UUID, &pImage->ImageUuid);
5782 if (RT_SUCCESS(rc))
5783 {
5784 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, VMDK_DDB_PARENT_UUID, &pImage->ParentUuid);
5785 if (RT_SUCCESS(rc))
5786 {
5787 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, VMDK_DDB_MODIFICATION_UUID,
5788 &pImage->ModificationUuid);
5789 if (RT_SUCCESS(rc))
5790 {
5791 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor, VMDK_DDB_PARENT_MODIFICATION_UUID,
5792 &pImage->ParentModificationUuid);
5793 if (RT_FAILURE(rc))
5794 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
5795 N_("VMDK: error storing parent modification UUID in new descriptor in '%s'"), pImage->pszFilename);
5796 }
5797 else
5798 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
5799 N_("VMDK: error storing modification UUID in new descriptor in '%s'"), pImage->pszFilename);
5800 }
5801 else
5802 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
5803 N_("VMDK: error storing parent image UUID in new descriptor in '%s'"), pImage->pszFilename);
5804 }
5805 else
5806 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
5807 N_("VMDK: error storing image UUID in new descriptor in '%s'"), pImage->pszFilename);
5808
5809 return rc;
5810}
5811
5812/**
5813 * Internal: The actual code for creating any VMDK variant currently in
5814 * existence on hosted environments.
5815 */
5816static int vmdkCreateImage(PVMDKIMAGE pImage, uint64_t cbSize,
5817 unsigned uImageFlags, const char *pszComment,
5818 PCVDGEOMETRY pPCHSGeometry,
5819 PCVDGEOMETRY pLCHSGeometry, PCRTUUID pUuid,
5820 PVDINTERFACEPROGRESS pIfProgress,
5821 unsigned uPercentStart, unsigned uPercentSpan)
5822{
5823 pImage->uImageFlags = uImageFlags;
5824
5825 pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk);
5826 pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage);
5827 AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER);
5828
5829 int rc = vmdkCreateDescriptor(pImage, pImage->pDescData, pImage->cbDescAlloc,
5830 &pImage->Descriptor);
5831 if (RT_SUCCESS(rc))
5832 {
5833 if (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)
5834 {
5835 /* Raw disk image (includes raw partition). */
5836 PVDISKRAW pRaw = NULL;
5837 rc = vmdkMakeRawDescriptor(pImage, &pRaw);
5838 if (RT_FAILURE(rc))
5839 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create raw descriptor for '%s'"),
5840 pImage->pszFilename);
5841 if (!cbSize)
5842 cbSize = pImage->cbSize;
5843
5844 rc = vmdkCreateRawImage(pImage, pRaw, cbSize);
5845 vmdkRawDescFree(pRaw);
5846 }
5847 else if (uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
5848 {
5849 /* Stream optimized sparse image (monolithic). */
5850 rc = vmdkCreateStreamImage(pImage, cbSize);
5851 }
5852 else
5853 {
5854 /* Regular fixed or sparse image (monolithic or split). */
5855 rc = vmdkCreateRegularImage(pImage, cbSize, uImageFlags,
5856 pIfProgress, uPercentStart,
5857 uPercentSpan * 95 / 100);
5858 }
5859
5860 if (RT_SUCCESS(rc))
5861 {
5862 vdIfProgress(pIfProgress, uPercentStart + uPercentSpan * 98 / 100);
5863
5864 pImage->cbSize = cbSize;
5865
5866 for (unsigned i = 0; i < pImage->cExtents; i++)
5867 {
5868 PVMDKEXTENT pExtent = &pImage->pExtents[i];
5869
5870 rc = vmdkDescExtInsert(pImage, &pImage->Descriptor, pExtent->enmAccess,
5871 pExtent->cNominalSectors, pExtent->enmType,
5872 pExtent->pszBasename, pExtent->uSectorOffset);
5873 if (RT_FAILURE(rc))
5874 {
5875 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not insert the extent list into descriptor in '%s'"), pImage->pszFilename);
5876 break;
5877 }
5878 }
5879
5880 if (RT_SUCCESS(rc))
5881 vmdkDescExtRemoveDummy(pImage, &pImage->Descriptor);
5882
5883 pImage->LCHSGeometry = *pLCHSGeometry;
5884 pImage->PCHSGeometry = *pPCHSGeometry;
5885
5886 if (RT_SUCCESS(rc))
5887 {
5888 if ( pPCHSGeometry->cCylinders != 0
5889 && pPCHSGeometry->cHeads != 0
5890 && pPCHSGeometry->cSectors != 0)
5891 rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry);
5892 else if (uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)
5893 {
5894 VDGEOMETRY RawDiskPCHSGeometry;
5895 RawDiskPCHSGeometry.cCylinders = (uint32_t)RT_MIN(pImage->cbSize / 512 / 16 / 63, 16383);
5896 RawDiskPCHSGeometry.cHeads = 16;
5897 RawDiskPCHSGeometry.cSectors = 63;
5898 rc = vmdkDescSetPCHSGeometry(pImage, &RawDiskPCHSGeometry);
5899 }
5900 }
5901
5902 if ( RT_SUCCESS(rc)
5903 && pLCHSGeometry->cCylinders != 0
5904 && pLCHSGeometry->cHeads != 0
5905 && pLCHSGeometry->cSectors != 0)
5906 rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry);
5907
5908 pImage->ImageUuid = *pUuid;
5909 RTUuidClear(&pImage->ParentUuid);
5910 RTUuidClear(&pImage->ModificationUuid);
5911 RTUuidClear(&pImage->ParentModificationUuid);
5912
5913 if (RT_SUCCESS(rc))
5914 rc = vmdkCreateImageDdbUuidsInit(pImage);
5915
5916 if (RT_SUCCESS(rc))
5917 rc = vmdkAllocateGrainTableCache(pImage);
5918
5919 if (RT_SUCCESS(rc))
5920 {
5921 rc = vmdkSetImageComment(pImage, pszComment);
5922 if (RT_FAILURE(rc))
5923 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot set image comment in '%s'"), pImage->pszFilename);
5924 }
5925
5926 if (RT_SUCCESS(rc))
5927 {
5928 vdIfProgress(pIfProgress, uPercentStart + uPercentSpan * 99 / 100);
5929
5930 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
5931 {
5932 /* streamOptimized is a bit special, we cannot trigger the flush
5933 * until all data has been written. So we write the necessary
5934 * information explicitly. */
5935 pImage->pExtents[0].cDescriptorSectors = VMDK_BYTE2SECTOR(RT_ALIGN_64( pImage->Descriptor.aLines[pImage->Descriptor.cLines]
5936 - pImage->Descriptor.aLines[0], 512));
5937 rc = vmdkWriteMetaSparseExtent(pImage, &pImage->pExtents[0], 0, NULL);
5938 if (RT_SUCCESS(rc))
5939 {
5940 rc = vmdkWriteDescriptor(pImage, NULL);
5941 if (RT_FAILURE(rc))
5942 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK descriptor in '%s'"), pImage->pszFilename);
5943 }
5944 else
5945 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write VMDK header in '%s'"), pImage->pszFilename);
5946 }
5947 else
5948 rc = vmdkFlushImage(pImage, NULL);
5949 }
5950 }
5951 }
5952 else
5953 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not create new descriptor in '%s'"), pImage->pszFilename);
5954
5955
5956 if (RT_SUCCESS(rc))
5957 {
5958 PVDREGIONDESC pRegion = &pImage->RegionList.aRegions[0];
5959 pImage->RegionList.fFlags = 0;
5960 pImage->RegionList.cRegions = 1;
5961
5962 pRegion->offRegion = 0; /* Disk start. */
5963 pRegion->cbBlock = 512;
5964 pRegion->enmDataForm = VDREGIONDATAFORM_RAW;
5965 pRegion->enmMetadataForm = VDREGIONMETADATAFORM_NONE;
5966 pRegion->cbData = 512;
5967 pRegion->cbMetadata = 0;
5968 pRegion->cRegionBlocksOrBytes = pImage->cbSize;
5969
5970 vdIfProgress(pIfProgress, uPercentStart + uPercentSpan);
5971 }
5972 else
5973 vmdkFreeImage(pImage, rc != VERR_ALREADY_EXISTS, false /*fFlush*/);
5974 return rc;
5975}
5976
5977/**
5978 * Internal: Update image comment.
5979 */
5980static int vmdkSetImageComment(PVMDKIMAGE pImage, const char *pszComment)
5981{
5982 char *pszCommentEncoded = NULL;
5983 if (pszComment)
5984 {
5985 pszCommentEncoded = vmdkEncodeString(pszComment);
5986 if (!pszCommentEncoded)
5987 return VERR_NO_MEMORY;
5988 }
5989
5990 int rc = vmdkDescDDBSetStr(pImage, &pImage->Descriptor,
5991 "ddb.comment", pszCommentEncoded);
5992 if (pszCommentEncoded)
5993 RTStrFree(pszCommentEncoded);
5994 if (RT_FAILURE(rc))
5995 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error storing image comment in descriptor in '%s'"), pImage->pszFilename);
5996 return VINF_SUCCESS;
5997}
5998
5999/**
6000 * Internal. Clear the grain table buffer for real stream optimized writing.
6001 */
6002static void vmdkStreamClearGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent)
6003{
6004 uint32_t cCacheLines = RT_ALIGN(pExtent->cGTEntries, VMDK_GT_CACHELINE_SIZE) / VMDK_GT_CACHELINE_SIZE;
6005 for (uint32_t i = 0; i < cCacheLines; i++)
6006 memset(&pImage->pGTCache->aGTCache[i].aGTData[0], '\0',
6007 VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t));
6008}
6009
6010/**
6011 * Internal. Flush the grain table buffer for real stream optimized writing.
6012 */
6013static int vmdkStreamFlushGT(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
6014 uint32_t uGDEntry)
6015{
6016 int rc = VINF_SUCCESS;
6017 uint32_t cCacheLines = RT_ALIGN(pExtent->cGTEntries, VMDK_GT_CACHELINE_SIZE) / VMDK_GT_CACHELINE_SIZE;
6018
6019 /* VMware does not write out completely empty grain tables in the case
6020 * of streamOptimized images, which according to my interpretation of
6021 * the VMDK 1.1 spec is bending the rules. Since they do it and we can
6022 * handle it without problems do it the same way and save some bytes. */
6023 bool fAllZero = true;
6024 for (uint32_t i = 0; i < cCacheLines; i++)
6025 {
6026 /* Convert the grain table to little endian in place, as it will not
6027 * be used at all after this function has been called. */
6028 uint32_t *pGTTmp = &pImage->pGTCache->aGTCache[i].aGTData[0];
6029 for (uint32_t j = 0; j < VMDK_GT_CACHELINE_SIZE; j++, pGTTmp++)
6030 if (*pGTTmp)
6031 {
6032 fAllZero = false;
6033 break;
6034 }
6035 if (!fAllZero)
6036 break;
6037 }
6038 if (fAllZero)
6039 return VINF_SUCCESS;
6040
6041 uint64_t uFileOffset = pExtent->uAppendPosition;
6042 if (!uFileOffset)
6043 return VERR_INTERNAL_ERROR;
6044 /* Align to sector, as the previous write could have been any size. */
6045 uFileOffset = RT_ALIGN_64(uFileOffset, 512);
6046
6047 /* Grain table marker. */
6048 uint8_t aMarker[512];
6049 PVMDKMARKER pMarker = (PVMDKMARKER)&aMarker[0];
6050 memset(pMarker, '\0', sizeof(aMarker));
6051 pMarker->uSector = RT_H2LE_U64(VMDK_BYTE2SECTOR((uint64_t)pExtent->cGTEntries * sizeof(uint32_t)));
6052 pMarker->uType = RT_H2LE_U32(VMDK_MARKER_GT);
6053 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage, uFileOffset,
6054 aMarker, sizeof(aMarker));
6055 AssertRC(rc);
6056 uFileOffset += 512;
6057
6058 if (!pExtent->pGD || pExtent->pGD[uGDEntry])
6059 return VERR_INTERNAL_ERROR;
6060
6061 pExtent->pGD[uGDEntry] = VMDK_BYTE2SECTOR(uFileOffset);
6062
6063 for (uint32_t i = 0; i < cCacheLines; i++)
6064 {
6065 /* Convert the grain table to little endian in place, as it will not
6066 * be used at all after this function has been called. */
6067 uint32_t *pGTTmp = &pImage->pGTCache->aGTCache[i].aGTData[0];
6068 for (uint32_t j = 0; j < VMDK_GT_CACHELINE_SIZE; j++, pGTTmp++)
6069 *pGTTmp = RT_H2LE_U32(*pGTTmp);
6070
6071 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage, uFileOffset,
6072 &pImage->pGTCache->aGTCache[i].aGTData[0],
6073 VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t));
6074 uFileOffset += VMDK_GT_CACHELINE_SIZE * sizeof(uint32_t);
6075 if (RT_FAILURE(rc))
6076 break;
6077 }
6078 Assert(!(uFileOffset % 512));
6079 pExtent->uAppendPosition = RT_ALIGN_64(uFileOffset, 512);
6080 return rc;
6081}
6082
6083/**
6084 * Internal. Free all allocated space for representing an image, and optionally
6085 * delete the image from disk.
6086 */
6087static int vmdkFreeImage(PVMDKIMAGE pImage, bool fDelete, bool fFlush)
6088{
6089 int rc = VINF_SUCCESS;
6090
6091 /* Freeing a never allocated image (e.g. because the open failed) is
6092 * not signalled as an error. After all nothing bad happens. */
6093 if (pImage)
6094 {
6095 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
6096 {
6097 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
6098 {
6099 /* Check if all extents are clean. */
6100 for (unsigned i = 0; i < pImage->cExtents; i++)
6101 {
6102 Assert(!pImage->pExtents[i].fUncleanShutdown);
6103 }
6104 }
6105 else
6106 {
6107 /* Mark all extents as clean. */
6108 for (unsigned i = 0; i < pImage->cExtents; i++)
6109 {
6110 if ( pImage->pExtents[i].enmType == VMDKETYPE_HOSTED_SPARSE
6111 && pImage->pExtents[i].fUncleanShutdown)
6112 {
6113 pImage->pExtents[i].fUncleanShutdown = false;
6114 pImage->pExtents[i].fMetaDirty = true;
6115 }
6116
6117 /* From now on it's not safe to append any more data. */
6118 pImage->pExtents[i].uAppendPosition = 0;
6119 }
6120 }
6121 }
6122
6123 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
6124 {
6125 /* No need to write any pending data if the file will be deleted
6126 * or if the new file wasn't successfully created. */
6127 if ( !fDelete && pImage->pExtents
6128 && pImage->pExtents[0].cGTEntries
6129 && pImage->pExtents[0].uAppendPosition)
6130 {
6131 PVMDKEXTENT pExtent = &pImage->pExtents[0];
6132 uint32_t uLastGDEntry = pExtent->uLastGrainAccess / pExtent->cGTEntries;
6133 rc = vmdkStreamFlushGT(pImage, pExtent, uLastGDEntry);
6134 AssertRC(rc);
6135 vmdkStreamClearGT(pImage, pExtent);
6136 for (uint32_t i = uLastGDEntry + 1; i < pExtent->cGDEntries; i++)
6137 {
6138 rc = vmdkStreamFlushGT(pImage, pExtent, i);
6139 AssertRC(rc);
6140 }
6141
6142 uint64_t uFileOffset = pExtent->uAppendPosition;
6143 if (!uFileOffset)
6144 return VERR_INTERNAL_ERROR;
6145 uFileOffset = RT_ALIGN_64(uFileOffset, 512);
6146
6147 /* From now on it's not safe to append any more data. */
6148 pExtent->uAppendPosition = 0;
6149
6150 /* Grain directory marker. */
6151 uint8_t aMarker[512];
6152 PVMDKMARKER pMarker = (PVMDKMARKER)&aMarker[0];
6153 memset(pMarker, '\0', sizeof(aMarker));
6154 pMarker->uSector = VMDK_BYTE2SECTOR(RT_ALIGN_64(RT_H2LE_U64((uint64_t)pExtent->cGDEntries * sizeof(uint32_t)), 512));
6155 pMarker->uType = RT_H2LE_U32(VMDK_MARKER_GD);
6156 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage, uFileOffset,
6157 aMarker, sizeof(aMarker));
6158 AssertRC(rc);
6159 uFileOffset += 512;
6160
6161 /* Write grain directory in little endian style. The array will
6162 * not be used after this, so convert in place. */
6163 uint32_t *pGDTmp = pExtent->pGD;
6164 for (uint32_t i = 0; i < pExtent->cGDEntries; i++, pGDTmp++)
6165 *pGDTmp = RT_H2LE_U32(*pGDTmp);
6166 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
6167 uFileOffset, pExtent->pGD,
6168 pExtent->cGDEntries * sizeof(uint32_t));
6169 AssertRC(rc);
6170
6171 pExtent->uSectorGD = VMDK_BYTE2SECTOR(uFileOffset);
6172 pExtent->uSectorRGD = VMDK_BYTE2SECTOR(uFileOffset);
6173 uFileOffset = RT_ALIGN_64( uFileOffset
6174 + pExtent->cGDEntries * sizeof(uint32_t),
6175 512);
6176
6177 /* Footer marker. */
6178 memset(pMarker, '\0', sizeof(aMarker));
6179 pMarker->uSector = VMDK_BYTE2SECTOR(512);
6180 pMarker->uType = RT_H2LE_U32(VMDK_MARKER_FOOTER);
6181 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
6182 uFileOffset, aMarker, sizeof(aMarker));
6183 AssertRC(rc);
6184
6185 uFileOffset += 512;
6186 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, uFileOffset, NULL);
6187 AssertRC(rc);
6188
6189 uFileOffset += 512;
6190 /* End-of-stream marker. */
6191 memset(pMarker, '\0', sizeof(aMarker));
6192 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
6193 uFileOffset, aMarker, sizeof(aMarker));
6194 AssertRC(rc);
6195 }
6196 }
6197 else if (!fDelete && fFlush)
6198 vmdkFlushImage(pImage, NULL);
6199
6200 if (pImage->pExtents != NULL)
6201 {
6202 for (unsigned i = 0 ; i < pImage->cExtents; i++)
6203 {
6204 int rc2 = vmdkFreeExtentData(pImage, &pImage->pExtents[i], fDelete);
6205 if (RT_SUCCESS(rc))
6206 rc = rc2; /* Propogate any error when closing the file. */
6207 }
6208 RTMemFree(pImage->pExtents);
6209 pImage->pExtents = NULL;
6210 }
6211 pImage->cExtents = 0;
6212 if (pImage->pFile != NULL)
6213 {
6214 int rc2 = vmdkFileClose(pImage, &pImage->pFile, fDelete);
6215 if (RT_SUCCESS(rc))
6216 rc = rc2; /* Propogate any error when closing the file. */
6217 }
6218 int rc2 = vmdkFileCheckAllClose(pImage);
6219 if (RT_SUCCESS(rc))
6220 rc = rc2; /* Propogate any error when closing the file. */
6221
6222 if (pImage->pGTCache)
6223 {
6224 RTMemFree(pImage->pGTCache);
6225 pImage->pGTCache = NULL;
6226 }
6227 if (pImage->pDescData)
6228 {
6229 RTMemFree(pImage->pDescData);
6230 pImage->pDescData = NULL;
6231 }
6232 }
6233
6234 LogFlowFunc(("returns %Rrc\n", rc));
6235 return rc;
6236}
6237
6238/**
6239 * Internal. Flush image data (and metadata) to disk.
6240 */
6241static int vmdkFlushImage(PVMDKIMAGE pImage, PVDIOCTX pIoCtx)
6242{
6243 PVMDKEXTENT pExtent;
6244 int rc = VINF_SUCCESS;
6245
6246 /* Update descriptor if changed. */
6247 if (pImage->Descriptor.fDirty)
6248 rc = vmdkWriteDescriptor(pImage, pIoCtx);
6249
6250 if (RT_SUCCESS(rc))
6251 {
6252 for (unsigned i = 0; i < pImage->cExtents; i++)
6253 {
6254 pExtent = &pImage->pExtents[i];
6255 if (pExtent->pFile != NULL && pExtent->fMetaDirty)
6256 {
6257 switch (pExtent->enmType)
6258 {
6259 case VMDKETYPE_HOSTED_SPARSE:
6260 if (!pExtent->fFooter)
6261 rc = vmdkWriteMetaSparseExtent(pImage, pExtent, 0, pIoCtx);
6262 else
6263 {
6264 uint64_t uFileOffset = pExtent->uAppendPosition;
6265 /* Simply skip writing anything if the streamOptimized
6266 * image hasn't been just created. */
6267 if (!uFileOffset)
6268 break;
6269 uFileOffset = RT_ALIGN_64(uFileOffset, 512);
6270 rc = vmdkWriteMetaSparseExtent(pImage, pExtent,
6271 uFileOffset, pIoCtx);
6272 }
6273 break;
6274 case VMDKETYPE_VMFS:
6275 case VMDKETYPE_FLAT:
6276 /* Nothing to do. */
6277 break;
6278 case VMDKETYPE_ZERO:
6279 default:
6280 AssertMsgFailed(("extent with type %d marked as dirty\n",
6281 pExtent->enmType));
6282 break;
6283 }
6284 }
6285
6286 if (RT_FAILURE(rc))
6287 break;
6288
6289 switch (pExtent->enmType)
6290 {
6291 case VMDKETYPE_HOSTED_SPARSE:
6292 case VMDKETYPE_VMFS:
6293 case VMDKETYPE_FLAT:
6294 /** @todo implement proper path absolute check. */
6295 if ( pExtent->pFile != NULL
6296 && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
6297 && !(pExtent->pszBasename[0] == RTPATH_SLASH))
6298 rc = vdIfIoIntFileFlush(pImage->pIfIo, pExtent->pFile->pStorage, pIoCtx,
6299 NULL, NULL);
6300 break;
6301 case VMDKETYPE_ZERO:
6302 /* No need to do anything for this extent. */
6303 break;
6304 default:
6305 AssertMsgFailed(("unknown extent type %d\n", pExtent->enmType));
6306 break;
6307 }
6308 }
6309 }
6310
6311 return rc;
6312}
6313
6314/**
6315 * Internal. Find extent corresponding to the sector number in the disk.
6316 */
6317static int vmdkFindExtent(PVMDKIMAGE pImage, uint64_t offSector,
6318 PVMDKEXTENT *ppExtent, uint64_t *puSectorInExtent)
6319{
6320 PVMDKEXTENT pExtent = NULL;
6321 int rc = VINF_SUCCESS;
6322
6323 for (unsigned i = 0; i < pImage->cExtents; i++)
6324 {
6325 if (offSector < pImage->pExtents[i].cNominalSectors)
6326 {
6327 pExtent = &pImage->pExtents[i];
6328 *puSectorInExtent = offSector + pImage->pExtents[i].uSectorOffset;
6329 break;
6330 }
6331 offSector -= pImage->pExtents[i].cNominalSectors;
6332 }
6333
6334 if (pExtent)
6335 *ppExtent = pExtent;
6336 else
6337 rc = VERR_IO_SECTOR_NOT_FOUND;
6338
6339 return rc;
6340}
6341
6342/**
6343 * Internal. Hash function for placing the grain table hash entries.
6344 */
6345static uint32_t vmdkGTCacheHash(PVMDKGTCACHE pCache, uint64_t uSector,
6346 unsigned uExtent)
6347{
6348 /** @todo this hash function is quite simple, maybe use a better one which
6349 * scrambles the bits better. */
6350 return (uSector + uExtent) % pCache->cEntries;
6351}
6352
6353/**
6354 * Internal. Get sector number in the extent file from the relative sector
6355 * number in the extent.
6356 */
6357static int vmdkGetSector(PVMDKIMAGE pImage, PVDIOCTX pIoCtx,
6358 PVMDKEXTENT pExtent, uint64_t uSector,
6359 uint64_t *puExtentSector)
6360{
6361 PVMDKGTCACHE pCache = pImage->pGTCache;
6362 uint64_t uGDIndex, uGTSector, uGTBlock;
6363 uint32_t uGTHash, uGTBlockIndex;
6364 PVMDKGTCACHEENTRY pGTCacheEntry;
6365 uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
6366 int rc;
6367
6368 /* For newly created and readonly/sequentially opened streamOptimized
6369 * images this must be a no-op, as the grain directory is not there. */
6370 if ( ( pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
6371 && pExtent->uAppendPosition)
6372 || ( pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
6373 && pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY
6374 && pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
6375 {
6376 *puExtentSector = 0;
6377 return VINF_SUCCESS;
6378 }
6379
6380 uGDIndex = uSector / pExtent->cSectorsPerGDE;
6381 if (uGDIndex >= pExtent->cGDEntries)
6382 return VERR_OUT_OF_RANGE;
6383 uGTSector = pExtent->pGD[uGDIndex];
6384 if (!uGTSector)
6385 {
6386 /* There is no grain table referenced by this grain directory
6387 * entry. So there is absolutely no data in this area. */
6388 *puExtentSector = 0;
6389 return VINF_SUCCESS;
6390 }
6391
6392 uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
6393 uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
6394 pGTCacheEntry = &pCache->aGTCache[uGTHash];
6395 if ( pGTCacheEntry->uExtent != pExtent->uExtent
6396 || pGTCacheEntry->uGTBlock != uGTBlock)
6397 {
6398 /* Cache miss, fetch data from disk. */
6399 PVDMETAXFER pMetaXfer;
6400 rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pExtent->pFile->pStorage,
6401 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
6402 aGTDataTmp, sizeof(aGTDataTmp), pIoCtx, &pMetaXfer, NULL, NULL);
6403 if (RT_FAILURE(rc))
6404 return rc;
6405 /* We can release the metadata transfer immediately. */
6406 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
6407 pGTCacheEntry->uExtent = pExtent->uExtent;
6408 pGTCacheEntry->uGTBlock = uGTBlock;
6409 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
6410 pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
6411 }
6412 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
6413 uint32_t uGrainSector = pGTCacheEntry->aGTData[uGTBlockIndex];
6414 if (uGrainSector)
6415 *puExtentSector = uGrainSector + uSector % pExtent->cSectorsPerGrain;
6416 else
6417 *puExtentSector = 0;
6418 return VINF_SUCCESS;
6419}
6420
6421/**
6422 * Internal. Writes the grain and also if necessary the grain tables.
6423 * Uses the grain table cache as a true grain table.
6424 */
6425static int vmdkStreamAllocGrain(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
6426 uint64_t uSector, PVDIOCTX pIoCtx,
6427 uint64_t cbWrite)
6428{
6429 uint32_t uGrain;
6430 uint32_t uGDEntry, uLastGDEntry;
6431 uint32_t cbGrain = 0;
6432 uint32_t uCacheLine, uCacheEntry;
6433 const void *pData;
6434 int rc;
6435
6436 /* Very strict requirements: always write at least one full grain, with
6437 * proper alignment. Everything else would require reading of already
6438 * written data, which we don't support for obvious reasons. The only
6439 * exception is the last grain, and only if the image size specifies
6440 * that only some portion holds data. In any case the write must be
6441 * within the image limits, no "overshoot" allowed. */
6442 if ( cbWrite == 0
6443 || ( cbWrite < VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain)
6444 && pExtent->cNominalSectors - uSector >= pExtent->cSectorsPerGrain)
6445 || uSector % pExtent->cSectorsPerGrain
6446 || uSector + VMDK_BYTE2SECTOR(cbWrite) > pExtent->cNominalSectors)
6447 return VERR_INVALID_PARAMETER;
6448
6449 /* Clip write range to at most the rest of the grain. */
6450 cbWrite = RT_MIN(cbWrite, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSector % pExtent->cSectorsPerGrain));
6451
6452 /* Do not allow to go back. */
6453 uGrain = uSector / pExtent->cSectorsPerGrain;
6454 uCacheLine = uGrain % pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE;
6455 uCacheEntry = uGrain % VMDK_GT_CACHELINE_SIZE;
6456 uGDEntry = uGrain / pExtent->cGTEntries;
6457 uLastGDEntry = pExtent->uLastGrainAccess / pExtent->cGTEntries;
6458 if (uGrain < pExtent->uLastGrainAccess)
6459 return VERR_VD_VMDK_INVALID_WRITE;
6460
6461 /* Zero byte write optimization. Since we don't tell VBoxHDD that we need
6462 * to allocate something, we also need to detect the situation ourself. */
6463 if ( !(pImage->uOpenFlags & VD_OPEN_FLAGS_HONOR_ZEROES)
6464 && vdIfIoIntIoCtxIsZero(pImage->pIfIo, pIoCtx, cbWrite, true /* fAdvance */))
6465 return VINF_SUCCESS;
6466
6467 if (uGDEntry != uLastGDEntry)
6468 {
6469 rc = vmdkStreamFlushGT(pImage, pExtent, uLastGDEntry);
6470 if (RT_FAILURE(rc))
6471 return rc;
6472 vmdkStreamClearGT(pImage, pExtent);
6473 for (uint32_t i = uLastGDEntry + 1; i < uGDEntry; i++)
6474 {
6475 rc = vmdkStreamFlushGT(pImage, pExtent, i);
6476 if (RT_FAILURE(rc))
6477 return rc;
6478 }
6479 }
6480
6481 uint64_t uFileOffset;
6482 uFileOffset = pExtent->uAppendPosition;
6483 if (!uFileOffset)
6484 return VERR_INTERNAL_ERROR;
6485 /* Align to sector, as the previous write could have been any size. */
6486 uFileOffset = RT_ALIGN_64(uFileOffset, 512);
6487
6488 /* Paranoia check: extent type, grain table buffer presence and
6489 * grain table buffer space. Also grain table entry must be clear. */
6490 if ( pExtent->enmType != VMDKETYPE_HOSTED_SPARSE
6491 || !pImage->pGTCache
6492 || pExtent->cGTEntries > VMDK_GT_CACHE_SIZE * VMDK_GT_CACHELINE_SIZE
6493 || pImage->pGTCache->aGTCache[uCacheLine].aGTData[uCacheEntry])
6494 return VERR_INTERNAL_ERROR;
6495
6496 /* Update grain table entry. */
6497 pImage->pGTCache->aGTCache[uCacheLine].aGTData[uCacheEntry] = VMDK_BYTE2SECTOR(uFileOffset);
6498
6499 if (cbWrite != VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
6500 {
6501 vdIfIoIntIoCtxCopyFrom(pImage->pIfIo, pIoCtx, pExtent->pvGrain, cbWrite);
6502 memset((char *)pExtent->pvGrain + cbWrite, '\0',
6503 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbWrite);
6504 pData = pExtent->pvGrain;
6505 }
6506 else
6507 {
6508 RTSGSEG Segment;
6509 unsigned cSegments = 1;
6510 size_t cbSeg = 0;
6511
6512 cbSeg = vdIfIoIntIoCtxSegArrayCreate(pImage->pIfIo, pIoCtx, &Segment,
6513 &cSegments, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
6514 Assert(cbSeg == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
6515 pData = Segment.pvSeg;
6516 }
6517 rc = vmdkFileDeflateSync(pImage, pExtent, uFileOffset, pData,
6518 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
6519 uSector, &cbGrain);
6520 if (RT_FAILURE(rc))
6521 {
6522 pExtent->uGrainSectorAbs = 0;
6523 AssertRC(rc);
6524 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write compressed data block in '%s'"), pExtent->pszFullname);
6525 }
6526 pExtent->uLastGrainAccess = uGrain;
6527 pExtent->uAppendPosition += cbGrain;
6528
6529 return rc;
6530}
6531
6532/**
6533 * Internal: Updates the grain table during grain allocation.
6534 */
6535static int vmdkAllocGrainGTUpdate(PVMDKIMAGE pImage, PVMDKEXTENT pExtent, PVDIOCTX pIoCtx,
6536 PVMDKGRAINALLOCASYNC pGrainAlloc)
6537{
6538 int rc = VINF_SUCCESS;
6539 PVMDKGTCACHE pCache = pImage->pGTCache;
6540 uint32_t aGTDataTmp[VMDK_GT_CACHELINE_SIZE];
6541 uint32_t uGTHash, uGTBlockIndex;
6542 uint64_t uGTSector, uRGTSector, uGTBlock;
6543 uint64_t uSector = pGrainAlloc->uSector;
6544 PVMDKGTCACHEENTRY pGTCacheEntry;
6545
6546 LogFlowFunc(("pImage=%#p pExtent=%#p pCache=%#p pIoCtx=%#p pGrainAlloc=%#p\n",
6547 pImage, pExtent, pCache, pIoCtx, pGrainAlloc));
6548
6549 uGTSector = pGrainAlloc->uGTSector;
6550 uRGTSector = pGrainAlloc->uRGTSector;
6551 LogFlow(("uGTSector=%llu uRGTSector=%llu\n", uGTSector, uRGTSector));
6552
6553 /* Update the grain table (and the cache). */
6554 uGTBlock = uSector / (pExtent->cSectorsPerGrain * VMDK_GT_CACHELINE_SIZE);
6555 uGTHash = vmdkGTCacheHash(pCache, uGTBlock, pExtent->uExtent);
6556 pGTCacheEntry = &pCache->aGTCache[uGTHash];
6557 if ( pGTCacheEntry->uExtent != pExtent->uExtent
6558 || pGTCacheEntry->uGTBlock != uGTBlock)
6559 {
6560 /* Cache miss, fetch data from disk. */
6561 LogFlow(("Cache miss, fetch data from disk\n"));
6562 PVDMETAXFER pMetaXfer = NULL;
6563 rc = vdIfIoIntFileReadMeta(pImage->pIfIo, pExtent->pFile->pStorage,
6564 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
6565 aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
6566 &pMetaXfer, vmdkAllocGrainComplete, pGrainAlloc);
6567 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
6568 {
6569 pGrainAlloc->cIoXfersPending++;
6570 pGrainAlloc->fGTUpdateNeeded = true;
6571 /* Leave early, we will be called again after the read completed. */
6572 LogFlowFunc(("Metadata read in progress, leaving\n"));
6573 return rc;
6574 }
6575 else if (RT_FAILURE(rc))
6576 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot read allocated grain table entry in '%s'"), pExtent->pszFullname);
6577 vdIfIoIntMetaXferRelease(pImage->pIfIo, pMetaXfer);
6578 pGTCacheEntry->uExtent = pExtent->uExtent;
6579 pGTCacheEntry->uGTBlock = uGTBlock;
6580 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
6581 pGTCacheEntry->aGTData[i] = RT_LE2H_U32(aGTDataTmp[i]);
6582 }
6583 else
6584 {
6585 /* Cache hit. Convert grain table block back to disk format, otherwise
6586 * the code below will write garbage for all but the updated entry. */
6587 for (unsigned i = 0; i < VMDK_GT_CACHELINE_SIZE; i++)
6588 aGTDataTmp[i] = RT_H2LE_U32(pGTCacheEntry->aGTData[i]);
6589 }
6590 pGrainAlloc->fGTUpdateNeeded = false;
6591 uGTBlockIndex = (uSector / pExtent->cSectorsPerGrain) % VMDK_GT_CACHELINE_SIZE;
6592 aGTDataTmp[uGTBlockIndex] = RT_H2LE_U32(VMDK_BYTE2SECTOR(pGrainAlloc->uGrainOffset));
6593 pGTCacheEntry->aGTData[uGTBlockIndex] = VMDK_BYTE2SECTOR(pGrainAlloc->uGrainOffset);
6594 /* Update grain table on disk. */
6595 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
6596 VMDK_SECTOR2BYTE(uGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
6597 aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
6598 vmdkAllocGrainComplete, pGrainAlloc);
6599 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
6600 pGrainAlloc->cIoXfersPending++;
6601 else if (RT_FAILURE(rc))
6602 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write updated grain table in '%s'"), pExtent->pszFullname);
6603 if (pExtent->pRGD)
6604 {
6605 /* Update backup grain table on disk. */
6606 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
6607 VMDK_SECTOR2BYTE(uRGTSector) + (uGTBlock % (pExtent->cGTEntries / VMDK_GT_CACHELINE_SIZE)) * sizeof(aGTDataTmp),
6608 aGTDataTmp, sizeof(aGTDataTmp), pIoCtx,
6609 vmdkAllocGrainComplete, pGrainAlloc);
6610 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
6611 pGrainAlloc->cIoXfersPending++;
6612 else if (RT_FAILURE(rc))
6613 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write updated backup grain table in '%s'"), pExtent->pszFullname);
6614 }
6615
6616 LogFlowFunc(("leaving rc=%Rrc\n", rc));
6617 return rc;
6618}
6619
6620/**
6621 * Internal - complete the grain allocation by updating disk grain table if required.
6622 */
6623static DECLCALLBACK(int) vmdkAllocGrainComplete(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
6624{
6625 RT_NOREF1(rcReq);
6626 int rc = VINF_SUCCESS;
6627 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
6628 PVMDKGRAINALLOCASYNC pGrainAlloc = (PVMDKGRAINALLOCASYNC)pvUser;
6629
6630 LogFlowFunc(("pBackendData=%#p pIoCtx=%#p pvUser=%#p rcReq=%Rrc\n",
6631 pBackendData, pIoCtx, pvUser, rcReq));
6632
6633 pGrainAlloc->cIoXfersPending--;
6634 if (!pGrainAlloc->cIoXfersPending && pGrainAlloc->fGTUpdateNeeded)
6635 rc = vmdkAllocGrainGTUpdate(pImage, pGrainAlloc->pExtent, pIoCtx, pGrainAlloc);
6636
6637 if (!pGrainAlloc->cIoXfersPending)
6638 {
6639 /* Grain allocation completed. */
6640 RTMemFree(pGrainAlloc);
6641 }
6642
6643 LogFlowFunc(("Leaving rc=%Rrc\n", rc));
6644 return rc;
6645}
6646
6647/**
6648 * Internal. Allocates a new grain table (if necessary).
6649 */
6650static int vmdkAllocGrain(PVMDKIMAGE pImage, PVMDKEXTENT pExtent, PVDIOCTX pIoCtx,
6651 uint64_t uSector, uint64_t cbWrite)
6652{
6653 PVMDKGTCACHE pCache = pImage->pGTCache; NOREF(pCache);
6654 uint64_t uGDIndex, uGTSector, uRGTSector;
6655 uint64_t uFileOffset;
6656 PVMDKGRAINALLOCASYNC pGrainAlloc = NULL;
6657 int rc;
6658
6659 LogFlowFunc(("pCache=%#p pExtent=%#p pIoCtx=%#p uSector=%llu cbWrite=%llu\n",
6660 pCache, pExtent, pIoCtx, uSector, cbWrite));
6661
6662 pGrainAlloc = (PVMDKGRAINALLOCASYNC)RTMemAllocZ(sizeof(VMDKGRAINALLOCASYNC));
6663 if (!pGrainAlloc)
6664 return VERR_NO_MEMORY;
6665
6666 pGrainAlloc->pExtent = pExtent;
6667 pGrainAlloc->uSector = uSector;
6668
6669 uGDIndex = uSector / pExtent->cSectorsPerGDE;
6670 if (uGDIndex >= pExtent->cGDEntries)
6671 {
6672 RTMemFree(pGrainAlloc);
6673 return VERR_OUT_OF_RANGE;
6674 }
6675 uGTSector = pExtent->pGD[uGDIndex];
6676 if (pExtent->pRGD)
6677 uRGTSector = pExtent->pRGD[uGDIndex];
6678 else
6679 uRGTSector = 0; /**< avoid compiler warning */
6680 if (!uGTSector)
6681 {
6682 LogFlow(("Allocating new grain table\n"));
6683
6684 /* There is no grain table referenced by this grain directory
6685 * entry. So there is absolutely no data in this area. Allocate
6686 * a new grain table and put the reference to it in the GDs. */
6687 uFileOffset = pExtent->uAppendPosition;
6688 if (!uFileOffset)
6689 {
6690 RTMemFree(pGrainAlloc);
6691 return VERR_INTERNAL_ERROR;
6692 }
6693 Assert(!(uFileOffset % 512));
6694
6695 uFileOffset = RT_ALIGN_64(uFileOffset, 512);
6696 uGTSector = VMDK_BYTE2SECTOR(uFileOffset);
6697
6698 /* Normally the grain table is preallocated for hosted sparse extents
6699 * that support more than 32 bit sector numbers. So this shouldn't
6700 * ever happen on a valid extent. */
6701 if (uGTSector > UINT32_MAX)
6702 {
6703 RTMemFree(pGrainAlloc);
6704 return VERR_VD_VMDK_INVALID_HEADER;
6705 }
6706
6707 /* Write grain table by writing the required number of grain table
6708 * cache chunks. Allocate memory dynamically here or we flood the
6709 * metadata cache with very small entries. */
6710 size_t cbGTDataTmp = pExtent->cGTEntries * sizeof(uint32_t);
6711 uint32_t *paGTDataTmp = (uint32_t *)RTMemTmpAllocZ(cbGTDataTmp);
6712
6713 if (!paGTDataTmp)
6714 {
6715 RTMemFree(pGrainAlloc);
6716 return VERR_NO_MEMORY;
6717 }
6718
6719 memset(paGTDataTmp, '\0', cbGTDataTmp);
6720 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
6721 VMDK_SECTOR2BYTE(uGTSector),
6722 paGTDataTmp, cbGTDataTmp, pIoCtx,
6723 vmdkAllocGrainComplete, pGrainAlloc);
6724 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
6725 pGrainAlloc->cIoXfersPending++;
6726 else if (RT_FAILURE(rc))
6727 {
6728 RTMemTmpFree(paGTDataTmp);
6729 RTMemFree(pGrainAlloc);
6730 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write grain table allocation in '%s'"), pExtent->pszFullname);
6731 }
6732 pExtent->uAppendPosition = RT_ALIGN_64( pExtent->uAppendPosition
6733 + cbGTDataTmp, 512);
6734
6735 if (pExtent->pRGD)
6736 {
6737 AssertReturn(!uRGTSector, VERR_VD_VMDK_INVALID_HEADER);
6738 uFileOffset = pExtent->uAppendPosition;
6739 if (!uFileOffset)
6740 return VERR_INTERNAL_ERROR;
6741 Assert(!(uFileOffset % 512));
6742 uRGTSector = VMDK_BYTE2SECTOR(uFileOffset);
6743
6744 /* Normally the redundant grain table is preallocated for hosted
6745 * sparse extents that support more than 32 bit sector numbers. So
6746 * this shouldn't ever happen on a valid extent. */
6747 if (uRGTSector > UINT32_MAX)
6748 {
6749 RTMemTmpFree(paGTDataTmp);
6750 return VERR_VD_VMDK_INVALID_HEADER;
6751 }
6752
6753 /* Write grain table by writing the required number of grain table
6754 * cache chunks. Allocate memory dynamically here or we flood the
6755 * metadata cache with very small entries. */
6756 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
6757 VMDK_SECTOR2BYTE(uRGTSector),
6758 paGTDataTmp, cbGTDataTmp, pIoCtx,
6759 vmdkAllocGrainComplete, pGrainAlloc);
6760 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
6761 pGrainAlloc->cIoXfersPending++;
6762 else if (RT_FAILURE(rc))
6763 {
6764 RTMemTmpFree(paGTDataTmp);
6765 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain table allocation in '%s'"), pExtent->pszFullname);
6766 }
6767
6768 pExtent->uAppendPosition = pExtent->uAppendPosition + cbGTDataTmp;
6769 }
6770
6771 RTMemTmpFree(paGTDataTmp);
6772
6773 /* Update the grain directory on disk (doing it before writing the
6774 * grain table will result in a garbled extent if the operation is
6775 * aborted for some reason. Otherwise the worst that can happen is
6776 * some unused sectors in the extent. */
6777 uint32_t uGTSectorLE = RT_H2LE_U64(uGTSector);
6778 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
6779 VMDK_SECTOR2BYTE(pExtent->uSectorGD) + uGDIndex * sizeof(uGTSectorLE),
6780 &uGTSectorLE, sizeof(uGTSectorLE), pIoCtx,
6781 vmdkAllocGrainComplete, pGrainAlloc);
6782 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
6783 pGrainAlloc->cIoXfersPending++;
6784 else if (RT_FAILURE(rc))
6785 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write grain directory entry in '%s'"), pExtent->pszFullname);
6786 if (pExtent->pRGD)
6787 {
6788 uint32_t uRGTSectorLE = RT_H2LE_U64(uRGTSector);
6789 rc = vdIfIoIntFileWriteMeta(pImage->pIfIo, pExtent->pFile->pStorage,
6790 VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + uGDIndex * sizeof(uGTSectorLE),
6791 &uRGTSectorLE, sizeof(uRGTSectorLE), pIoCtx,
6792 vmdkAllocGrainComplete, pGrainAlloc);
6793 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
6794 pGrainAlloc->cIoXfersPending++;
6795 else if (RT_FAILURE(rc))
6796 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write backup grain directory entry in '%s'"), pExtent->pszFullname);
6797 }
6798
6799 /* As the final step update the in-memory copy of the GDs. */
6800 pExtent->pGD[uGDIndex] = uGTSector;
6801 if (pExtent->pRGD)
6802 pExtent->pRGD[uGDIndex] = uRGTSector;
6803 }
6804
6805 LogFlow(("uGTSector=%llu uRGTSector=%llu\n", uGTSector, uRGTSector));
6806 pGrainAlloc->uGTSector = uGTSector;
6807 pGrainAlloc->uRGTSector = uRGTSector;
6808
6809 uFileOffset = pExtent->uAppendPosition;
6810 if (!uFileOffset)
6811 return VERR_INTERNAL_ERROR;
6812 Assert(!(uFileOffset % 512));
6813
6814 pGrainAlloc->uGrainOffset = uFileOffset;
6815
6816 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
6817 {
6818 AssertMsgReturn(vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx),
6819 ("Accesses to stream optimized images must be synchronous\n"),
6820 VERR_INVALID_STATE);
6821
6822 if (cbWrite != VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
6823 return vdIfError(pImage->pIfError, VERR_INTERNAL_ERROR, RT_SRC_POS, N_("VMDK: not enough data for a compressed data block in '%s'"), pExtent->pszFullname);
6824
6825 /* Invalidate cache, just in case some code incorrectly allows mixing
6826 * of reads and writes. Normally shouldn't be needed. */
6827 pExtent->uGrainSectorAbs = 0;
6828
6829 /* Write compressed data block and the markers. */
6830 uint32_t cbGrain = 0;
6831 size_t cbSeg = 0;
6832 RTSGSEG Segment;
6833 unsigned cSegments = 1;
6834
6835 cbSeg = vdIfIoIntIoCtxSegArrayCreate(pImage->pIfIo, pIoCtx, &Segment,
6836 &cSegments, cbWrite);
6837 Assert(cbSeg == cbWrite);
6838
6839 rc = vmdkFileDeflateSync(pImage, pExtent, uFileOffset,
6840 Segment.pvSeg, cbWrite, uSector, &cbGrain);
6841 if (RT_FAILURE(rc))
6842 {
6843 AssertRC(rc);
6844 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write allocated compressed data block in '%s'"), pExtent->pszFullname);
6845 }
6846 pExtent->uLastGrainAccess = uSector / pExtent->cSectorsPerGrain;
6847 pExtent->uAppendPosition += cbGrain;
6848 }
6849 else
6850 {
6851 /* Write the data. Always a full grain, or we're in big trouble. */
6852 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pExtent->pFile->pStorage,
6853 uFileOffset, pIoCtx, cbWrite,
6854 vmdkAllocGrainComplete, pGrainAlloc);
6855 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
6856 pGrainAlloc->cIoXfersPending++;
6857 else if (RT_FAILURE(rc))
6858 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: cannot write allocated data block in '%s'"), pExtent->pszFullname);
6859
6860 pExtent->uAppendPosition += cbWrite;
6861 }
6862
6863 rc = vmdkAllocGrainGTUpdate(pImage, pExtent, pIoCtx, pGrainAlloc);
6864
6865 if (!pGrainAlloc->cIoXfersPending)
6866 {
6867 /* Grain allocation completed. */
6868 RTMemFree(pGrainAlloc);
6869 }
6870
6871 LogFlowFunc(("leaving rc=%Rrc\n", rc));
6872
6873 return rc;
6874}
6875
6876/**
6877 * Internal. Reads the contents by sequentially going over the compressed
6878 * grains (hoping that they are in sequence).
6879 */
6880static int vmdkStreamReadSequential(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
6881 uint64_t uSector, PVDIOCTX pIoCtx,
6882 uint64_t cbRead)
6883{
6884 int rc;
6885
6886 LogFlowFunc(("pImage=%#p pExtent=%#p uSector=%llu pIoCtx=%#p cbRead=%llu\n",
6887 pImage, pExtent, uSector, pIoCtx, cbRead));
6888
6889 AssertMsgReturn(vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx),
6890 ("Async I/O not supported for sequential stream optimized images\n"),
6891 VERR_INVALID_STATE);
6892
6893 /* Do not allow to go back. */
6894 uint32_t uGrain = uSector / pExtent->cSectorsPerGrain;
6895 if (uGrain < pExtent->uLastGrainAccess)
6896 return VERR_VD_VMDK_INVALID_STATE;
6897 pExtent->uLastGrainAccess = uGrain;
6898
6899 /* After a previous error do not attempt to recover, as it would need
6900 * seeking (in the general case backwards which is forbidden). */
6901 if (!pExtent->uGrainSectorAbs)
6902 return VERR_VD_VMDK_INVALID_STATE;
6903
6904 /* Check if we need to read something from the image or if what we have
6905 * in the buffer is good to fulfill the request. */
6906 if (!pExtent->cbGrainStreamRead || uGrain > pExtent->uGrain)
6907 {
6908 uint32_t uGrainSectorAbs = pExtent->uGrainSectorAbs
6909 + VMDK_BYTE2SECTOR(pExtent->cbGrainStreamRead);
6910
6911 /* Get the marker from the next data block - and skip everything which
6912 * is not a compressed grain. If it's a compressed grain which is for
6913 * the requested sector (or after), read it. */
6914 VMDKMARKER Marker;
6915 do
6916 {
6917 RT_ZERO(Marker);
6918 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
6919 VMDK_SECTOR2BYTE(uGrainSectorAbs),
6920 &Marker, RT_UOFFSETOF(VMDKMARKER, uType));
6921 if (RT_FAILURE(rc))
6922 return rc;
6923 Marker.uSector = RT_LE2H_U64(Marker.uSector);
6924 Marker.cbSize = RT_LE2H_U32(Marker.cbSize);
6925
6926 if (Marker.cbSize == 0)
6927 {
6928 /* A marker for something else than a compressed grain. */
6929 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
6930 VMDK_SECTOR2BYTE(uGrainSectorAbs)
6931 + RT_UOFFSETOF(VMDKMARKER, uType),
6932 &Marker.uType, sizeof(Marker.uType));
6933 if (RT_FAILURE(rc))
6934 return rc;
6935 Marker.uType = RT_LE2H_U32(Marker.uType);
6936 switch (Marker.uType)
6937 {
6938 case VMDK_MARKER_EOS:
6939 uGrainSectorAbs++;
6940 /* Read (or mostly skip) to the end of file. Uses the
6941 * Marker (LBA sector) as it is unused anyway. This
6942 * makes sure that really everything is read in the
6943 * success case. If this read fails it means the image
6944 * is truncated, but this is harmless so ignore. */
6945 vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
6946 VMDK_SECTOR2BYTE(uGrainSectorAbs)
6947 + 511,
6948 &Marker.uSector, 1);
6949 break;
6950 case VMDK_MARKER_GT:
6951 uGrainSectorAbs += 1 + VMDK_BYTE2SECTOR(pExtent->cGTEntries * sizeof(uint32_t));
6952 break;
6953 case VMDK_MARKER_GD:
6954 uGrainSectorAbs += 1 + VMDK_BYTE2SECTOR(RT_ALIGN(pExtent->cGDEntries * sizeof(uint32_t), 512));
6955 break;
6956 case VMDK_MARKER_FOOTER:
6957 uGrainSectorAbs += 2;
6958 break;
6959 case VMDK_MARKER_UNSPECIFIED:
6960 /* Skip over the contents of the unspecified marker
6961 * type 4 which exists in some vSphere created files. */
6962 /** @todo figure out what the payload means. */
6963 uGrainSectorAbs += 1;
6964 break;
6965 default:
6966 AssertMsgFailed(("VMDK: corrupted marker, type=%#x\n", Marker.uType));
6967 pExtent->uGrainSectorAbs = 0;
6968 return VERR_VD_VMDK_INVALID_STATE;
6969 }
6970 pExtent->cbGrainStreamRead = 0;
6971 }
6972 else
6973 {
6974 /* A compressed grain marker. If it is at/after what we're
6975 * interested in read and decompress data. */
6976 if (uSector > Marker.uSector + pExtent->cSectorsPerGrain)
6977 {
6978 uGrainSectorAbs += VMDK_BYTE2SECTOR(RT_ALIGN(Marker.cbSize + RT_UOFFSETOF(VMDKMARKER, uType), 512));
6979 continue;
6980 }
6981 uint64_t uLBA = 0;
6982 uint32_t cbGrainStreamRead = 0;
6983 rc = vmdkFileInflateSync(pImage, pExtent,
6984 VMDK_SECTOR2BYTE(uGrainSectorAbs),
6985 pExtent->pvGrain,
6986 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
6987 &Marker, &uLBA, &cbGrainStreamRead);
6988 if (RT_FAILURE(rc))
6989 {
6990 pExtent->uGrainSectorAbs = 0;
6991 return rc;
6992 }
6993 if ( pExtent->uGrain
6994 && uLBA / pExtent->cSectorsPerGrain <= pExtent->uGrain)
6995 {
6996 pExtent->uGrainSectorAbs = 0;
6997 return VERR_VD_VMDK_INVALID_STATE;
6998 }
6999 pExtent->uGrain = uLBA / pExtent->cSectorsPerGrain;
7000 pExtent->cbGrainStreamRead = cbGrainStreamRead;
7001 break;
7002 }
7003 } while (Marker.uType != VMDK_MARKER_EOS);
7004
7005 pExtent->uGrainSectorAbs = uGrainSectorAbs;
7006
7007 if (!pExtent->cbGrainStreamRead && Marker.uType == VMDK_MARKER_EOS)
7008 {
7009 pExtent->uGrain = UINT32_MAX;
7010 /* Must set a non-zero value for pExtent->cbGrainStreamRead or
7011 * the next read would try to get more data, and we're at EOF. */
7012 pExtent->cbGrainStreamRead = 1;
7013 }
7014 }
7015
7016 if (pExtent->uGrain > uSector / pExtent->cSectorsPerGrain)
7017 {
7018 /* The next data block we have is not for this area, so just return
7019 * that there is no data. */
7020 LogFlowFunc(("returns VERR_VD_BLOCK_FREE\n"));
7021 return VERR_VD_BLOCK_FREE;
7022 }
7023
7024 uint32_t uSectorInGrain = uSector % pExtent->cSectorsPerGrain;
7025 vdIfIoIntIoCtxCopyTo(pImage->pIfIo, pIoCtx,
7026 (uint8_t *)pExtent->pvGrain + VMDK_SECTOR2BYTE(uSectorInGrain),
7027 cbRead);
7028 LogFlowFunc(("returns VINF_SUCCESS\n"));
7029 return VINF_SUCCESS;
7030}
7031
7032/**
7033 * Replaces a fragment of a string with the specified string.
7034 *
7035 * @returns Pointer to the allocated UTF-8 string.
7036 * @param pszWhere UTF-8 string to search in.
7037 * @param pszWhat UTF-8 string to search for.
7038 * @param pszByWhat UTF-8 string to replace the found string with.
7039 *
7040 * @note r=bird: This is only used by vmdkRenameWorker(). The first use is
7041 * for updating the base name in the descriptor, the second is for
7042 * generating new filenames for extents. This code borked when
7043 * RTPathAbs started correcting the driver letter case on windows,
7044 * when strstr failed because the pExtent->pszFullname was not
7045 * subjected to RTPathAbs but while pExtent->pszFullname was. I fixed
7046 * this by apply RTPathAbs to the places it wasn't applied.
7047 *
7048 * However, this highlights some undocumented ASSUMPTIONS as well as
7049 * terrible short commings of the approach.
7050 *
7051 * Given the right filename, it may also screw up the descriptor. Take
7052 * the descriptor text 'RW 2048 SPARSE "Test0.vmdk"' for instance,
7053 * we'll be asked to replace "Test0" with something, no problem. No,
7054 * imagine 'RW 2048 SPARSE "SPARSE.vmdk"', 'RW 2048 SPARSE "RW.vmdk"'
7055 * or 'RW 2048 SPARSE "2048.vmdk"', and the strstr approach falls on
7056 * its bum. The descriptor string must be parsed and reconstructed,
7057 * the lazy strstr approach doesn't cut it.
7058 *
7059 * I'm also curious as to what would be the correct escaping of '"' in
7060 * the file name and how that is supposed to be handled, because it
7061 * needs to be or such names must be rejected in several places (maybe
7062 * they are, I didn't check).
7063 *
7064 * When this function is used to replace the start of a path, I think
7065 * the assumption from the prep/setup code is that we kind of knows
7066 * what we're working on (I could be wrong). However, using strstr
7067 * instead of strncmp/RTStrNICmp makes no sense and isn't future proof.
7068 * Especially on unix systems, weird stuff could happen if someone
7069 * unwittingly tinkers with the prep/setup code. What should really be
7070 * done here is using a new RTPathStartEx function that (via flags)
7071 * allows matching partial final component and returns the length of
7072 * what it matched up (in case it skipped slashes and '.' components).
7073 *
7074 */
7075static char *vmdkStrReplace(const char *pszWhere, const char *pszWhat,
7076 const char *pszByWhat)
7077{
7078 AssertPtr(pszWhere);
7079 AssertPtr(pszWhat);
7080 AssertPtr(pszByWhat);
7081 const char *pszFoundStr = strstr(pszWhere, pszWhat);
7082 if (!pszFoundStr)
7083 {
7084 LogFlowFunc(("Failed to find '%s' in '%s'!\n", pszWhat, pszWhere));
7085 return NULL;
7086 }
7087 size_t cbFinal = strlen(pszWhere) + 1 + strlen(pszByWhat) - strlen(pszWhat);
7088 char *pszNewStr = RTStrAlloc(cbFinal);
7089 if (pszNewStr)
7090 {
7091 char *pszTmp = pszNewStr;
7092 memcpy(pszTmp, pszWhere, pszFoundStr - pszWhere);
7093 pszTmp += pszFoundStr - pszWhere;
7094 memcpy(pszTmp, pszByWhat, strlen(pszByWhat));
7095 pszTmp += strlen(pszByWhat);
7096 strcpy(pszTmp, pszFoundStr + strlen(pszWhat));
7097 }
7098 return pszNewStr;
7099}
7100
7101
7102/** @copydoc VDIMAGEBACKEND::pfnProbe */
7103static DECLCALLBACK(int) vmdkProbe(const char *pszFilename, PVDINTERFACE pVDIfsDisk,
7104 PVDINTERFACE pVDIfsImage, VDTYPE enmDesiredType, VDTYPE *penmType)
7105{
7106 RT_NOREF(enmDesiredType);
7107 LogFlowFunc(("pszFilename=\"%s\" pVDIfsDisk=%#p pVDIfsImage=%#p penmType=%#p\n",
7108 pszFilename, pVDIfsDisk, pVDIfsImage, penmType));
7109 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
7110 AssertReturn(*pszFilename != '\0', VERR_INVALID_PARAMETER);
7111
7112 int rc = VINF_SUCCESS;
7113 PVMDKIMAGE pImage = (PVMDKIMAGE)RTMemAllocZ(RT_UOFFSETOF(VMDKIMAGE, RegionList.aRegions[1]));
7114 if (RT_LIKELY(pImage))
7115 {
7116 pImage->pszFilename = pszFilename;
7117 pImage->pFile = NULL;
7118 pImage->pExtents = NULL;
7119 pImage->pFiles = NULL;
7120 pImage->pGTCache = NULL;
7121 pImage->pDescData = NULL;
7122 pImage->pVDIfsDisk = pVDIfsDisk;
7123 pImage->pVDIfsImage = pVDIfsImage;
7124 /** @todo speed up this test open (VD_OPEN_FLAGS_INFO) by skipping as
7125 * much as possible in vmdkOpenImage. */
7126 rc = vmdkOpenImage(pImage, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY);
7127 vmdkFreeImage(pImage, false, false /*fFlush*/);
7128 RTMemFree(pImage);
7129
7130 if (RT_SUCCESS(rc))
7131 *penmType = VDTYPE_HDD;
7132 }
7133 else
7134 rc = VERR_NO_MEMORY;
7135
7136 LogFlowFunc(("returns %Rrc\n", rc));
7137 return rc;
7138}
7139
7140/** @copydoc VDIMAGEBACKEND::pfnOpen */
7141static DECLCALLBACK(int) vmdkOpen(const char *pszFilename, unsigned uOpenFlags,
7142 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
7143 VDTYPE enmType, void **ppBackendData)
7144{
7145 RT_NOREF1(enmType); /**< @todo r=klaus make use of the type info. */
7146
7147 LogFlowFunc(("pszFilename=\"%s\" uOpenFlags=%#x pVDIfsDisk=%#p pVDIfsImage=%#p enmType=%u ppBackendData=%#p\n",
7148 pszFilename, uOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
7149 int rc;
7150
7151 /* Check open flags. All valid flags are supported. */
7152 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
7153 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
7154 AssertReturn(*pszFilename != '\0', VERR_INVALID_PARAMETER);
7155
7156
7157 PVMDKIMAGE pImage = (PVMDKIMAGE)RTMemAllocZ(RT_UOFFSETOF(VMDKIMAGE, RegionList.aRegions[1]));
7158 if (RT_LIKELY(pImage))
7159 {
7160 pImage->pszFilename = pszFilename;
7161 pImage->pFile = NULL;
7162 pImage->pExtents = NULL;
7163 pImage->pFiles = NULL;
7164 pImage->pGTCache = NULL;
7165 pImage->pDescData = NULL;
7166 pImage->pVDIfsDisk = pVDIfsDisk;
7167 pImage->pVDIfsImage = pVDIfsImage;
7168
7169 rc = vmdkOpenImage(pImage, uOpenFlags);
7170 if (RT_SUCCESS(rc))
7171 *ppBackendData = pImage;
7172 else
7173 RTMemFree(pImage);
7174 }
7175 else
7176 rc = VERR_NO_MEMORY;
7177
7178 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
7179 return rc;
7180}
7181
7182/** @copydoc VDIMAGEBACKEND::pfnCreate */
7183static DECLCALLBACK(int) vmdkCreate(const char *pszFilename, uint64_t cbSize,
7184 unsigned uImageFlags, const char *pszComment,
7185 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
7186 PCRTUUID pUuid, unsigned uOpenFlags,
7187 unsigned uPercentStart, unsigned uPercentSpan,
7188 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
7189 PVDINTERFACE pVDIfsOperation, VDTYPE enmType,
7190 void **ppBackendData)
7191{
7192 LogFlowFunc(("pszFilename=\"%s\" cbSize=%llu uImageFlags=%#x pszComment=\"%s\" pPCHSGeometry=%#p pLCHSGeometry=%#p Uuid=%RTuuid uOpenFlags=%#x uPercentStart=%u uPercentSpan=%u pVDIfsDisk=%#p pVDIfsImage=%#p pVDIfsOperation=%#p enmType=%u ppBackendData=%#p\n",
7193 pszFilename, cbSize, uImageFlags, pszComment, pPCHSGeometry, pLCHSGeometry, pUuid, uOpenFlags, uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation, enmType, ppBackendData));
7194 int rc;
7195
7196 /* Check the VD container type and image flags. */
7197 if ( enmType != VDTYPE_HDD
7198 || (uImageFlags & ~VD_VMDK_IMAGE_FLAGS_MASK) != 0)
7199 return VERR_VD_INVALID_TYPE;
7200
7201 /* Check size. Maximum 256TB-64K for sparse images, otherwise unlimited. */
7202 if ( !(uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)
7203 && ( !cbSize
7204 || (!(uImageFlags & VD_IMAGE_FLAGS_FIXED) && cbSize >= _1T * 256 - _64K)))
7205 return VERR_VD_INVALID_SIZE;
7206
7207 /* Check image flags for invalid combinations. */
7208 if ( (uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
7209 && (uImageFlags & ~(VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED | VD_IMAGE_FLAGS_DIFF)))
7210 return VERR_INVALID_PARAMETER;
7211
7212 /* Check open flags. All valid flags are supported. */
7213 AssertReturn(!(uOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_PARAMETER);
7214 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
7215 AssertReturn(*pszFilename != '\0', VERR_INVALID_PARAMETER);
7216 AssertPtrReturn(pPCHSGeometry, VERR_INVALID_POINTER);
7217 AssertPtrReturn(pLCHSGeometry, VERR_INVALID_POINTER);
7218 AssertReturn(!( uImageFlags & VD_VMDK_IMAGE_FLAGS_ESX
7219 && !(uImageFlags & VD_IMAGE_FLAGS_FIXED)),
7220 VERR_INVALID_PARAMETER);
7221
7222 PVMDKIMAGE pImage = (PVMDKIMAGE)RTMemAllocZ(RT_UOFFSETOF(VMDKIMAGE, RegionList.aRegions[1]));
7223 if (RT_LIKELY(pImage))
7224 {
7225 PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
7226
7227 pImage->pszFilename = pszFilename;
7228 pImage->pFile = NULL;
7229 pImage->pExtents = NULL;
7230 pImage->pFiles = NULL;
7231 pImage->pGTCache = NULL;
7232 pImage->pDescData = NULL;
7233 pImage->pVDIfsDisk = pVDIfsDisk;
7234 pImage->pVDIfsImage = pVDIfsImage;
7235 /* Descriptors for split images can be pretty large, especially if the
7236 * filename is long. So prepare for the worst, and allocate quite some
7237 * memory for the descriptor in this case. */
7238 if (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G)
7239 pImage->cbDescAlloc = VMDK_SECTOR2BYTE(200);
7240 else
7241 pImage->cbDescAlloc = VMDK_SECTOR2BYTE(20);
7242 pImage->pDescData = (char *)RTMemAllocZ(pImage->cbDescAlloc);
7243 if (RT_LIKELY(pImage->pDescData))
7244 {
7245 rc = vmdkCreateImage(pImage, cbSize, uImageFlags, pszComment,
7246 pPCHSGeometry, pLCHSGeometry, pUuid,
7247 pIfProgress, uPercentStart, uPercentSpan);
7248 if (RT_SUCCESS(rc))
7249 {
7250 /* So far the image is opened in read/write mode. Make sure the
7251 * image is opened in read-only mode if the caller requested that. */
7252 if (uOpenFlags & VD_OPEN_FLAGS_READONLY)
7253 {
7254 vmdkFreeImage(pImage, false, true /*fFlush*/);
7255 rc = vmdkOpenImage(pImage, uOpenFlags);
7256 }
7257
7258 if (RT_SUCCESS(rc))
7259 *ppBackendData = pImage;
7260 }
7261
7262 if (RT_FAILURE(rc))
7263 RTMemFree(pImage->pDescData);
7264 }
7265 else
7266 rc = VERR_NO_MEMORY;
7267
7268 if (RT_FAILURE(rc))
7269 RTMemFree(pImage);
7270 }
7271 else
7272 rc = VERR_NO_MEMORY;
7273
7274 LogFlowFunc(("returns %Rrc (pBackendData=%#p)\n", rc, *ppBackendData));
7275 return rc;
7276}
7277
7278/**
7279 * Prepares the state for renaming a VMDK image, setting up the state and allocating
7280 * memory.
7281 *
7282 * @returns VBox status code.
7283 * @param pImage VMDK image instance.
7284 * @param pRenameState The state to initialize.
7285 * @param pszFilename The new filename.
7286 */
7287static int vmdkRenameStatePrepare(PVMDKIMAGE pImage, PVMDKRENAMESTATE pRenameState, const char *pszFilename)
7288{
7289 AssertReturn(RTPathFilename(pszFilename) != NULL, VERR_INVALID_PARAMETER);
7290
7291 int rc = VINF_SUCCESS;
7292
7293 memset(&pRenameState->DescriptorCopy, 0, sizeof(pRenameState->DescriptorCopy));
7294
7295 /*
7296 * Allocate an array to store both old and new names of renamed files
7297 * in case we have to roll back the changes. Arrays are initialized
7298 * with zeros. We actually save stuff when and if we change it.
7299 */
7300 pRenameState->cExtents = pImage->cExtents;
7301 pRenameState->apszOldName = (char **)RTMemTmpAllocZ((pRenameState->cExtents + 1) * sizeof(char *));
7302 pRenameState->apszNewName = (char **)RTMemTmpAllocZ((pRenameState->cExtents + 1) * sizeof(char *));
7303 pRenameState->apszNewLines = (char **)RTMemTmpAllocZ(pRenameState->cExtents * sizeof(char *));
7304 if ( pRenameState->apszOldName
7305 && pRenameState->apszNewName
7306 && pRenameState->apszNewLines)
7307 {
7308 /* Save the descriptor size and position. */
7309 if (pImage->pDescData)
7310 {
7311 /* Separate descriptor file. */
7312 pRenameState->fEmbeddedDesc = false;
7313 }
7314 else
7315 {
7316 /* Embedded descriptor file. */
7317 pRenameState->ExtentCopy = pImage->pExtents[0];
7318 pRenameState->fEmbeddedDesc = true;
7319 }
7320
7321 /* Save the descriptor content. */
7322 pRenameState->DescriptorCopy.cLines = pImage->Descriptor.cLines;
7323 for (unsigned i = 0; i < pRenameState->DescriptorCopy.cLines; i++)
7324 {
7325 pRenameState->DescriptorCopy.aLines[i] = RTStrDup(pImage->Descriptor.aLines[i]);
7326 if (!pRenameState->DescriptorCopy.aLines[i])
7327 {
7328 rc = VERR_NO_MEMORY;
7329 break;
7330 }
7331 }
7332
7333 if (RT_SUCCESS(rc))
7334 {
7335 /* Prepare both old and new base names used for string replacement. */
7336 pRenameState->pszNewBaseName = RTStrDup(RTPathFilename(pszFilename));
7337 AssertReturn(pRenameState->pszNewBaseName, VERR_NO_STR_MEMORY);
7338 RTPathStripSuffix(pRenameState->pszNewBaseName);
7339
7340 pRenameState->pszOldBaseName = RTStrDup(RTPathFilename(pImage->pszFilename));
7341 AssertReturn(pRenameState->pszOldBaseName, VERR_NO_STR_MEMORY);
7342 RTPathStripSuffix(pRenameState->pszOldBaseName);
7343
7344 /* Prepare both old and new full names used for string replacement.
7345 Note! Must abspath the stuff here, so the strstr weirdness later in
7346 the renaming process get a match against abspath'ed extent paths.
7347 See RTPathAbsDup call in vmdkDescriptorReadSparse(). */
7348 pRenameState->pszNewFullName = RTPathAbsDup(pszFilename);
7349 AssertReturn(pRenameState->pszNewFullName, VERR_NO_STR_MEMORY);
7350 RTPathStripSuffix(pRenameState->pszNewFullName);
7351
7352 pRenameState->pszOldFullName = RTPathAbsDup(pImage->pszFilename);
7353 AssertReturn(pRenameState->pszOldFullName, VERR_NO_STR_MEMORY);
7354 RTPathStripSuffix(pRenameState->pszOldFullName);
7355
7356 /* Save the old name for easy access to the old descriptor file. */
7357 pRenameState->pszOldDescName = RTStrDup(pImage->pszFilename);
7358 AssertReturn(pRenameState->pszOldDescName, VERR_NO_STR_MEMORY);
7359
7360 /* Save old image name. */
7361 pRenameState->pszOldImageName = pImage->pszFilename;
7362 }
7363 }
7364 else
7365 rc = VERR_NO_TMP_MEMORY;
7366
7367 return rc;
7368}
7369
7370/**
7371 * Destroys the given rename state, freeing all allocated memory.
7372 *
7373 * @param pRenameState The rename state to destroy.
7374 */
7375static void vmdkRenameStateDestroy(PVMDKRENAMESTATE pRenameState)
7376{
7377 for (unsigned i = 0; i < pRenameState->DescriptorCopy.cLines; i++)
7378 if (pRenameState->DescriptorCopy.aLines[i])
7379 RTStrFree(pRenameState->DescriptorCopy.aLines[i]);
7380 if (pRenameState->apszOldName)
7381 {
7382 for (unsigned i = 0; i <= pRenameState->cExtents; i++)
7383 if (pRenameState->apszOldName[i])
7384 RTStrFree(pRenameState->apszOldName[i]);
7385 RTMemTmpFree(pRenameState->apszOldName);
7386 }
7387 if (pRenameState->apszNewName)
7388 {
7389 for (unsigned i = 0; i <= pRenameState->cExtents; i++)
7390 if (pRenameState->apszNewName[i])
7391 RTStrFree(pRenameState->apszNewName[i]);
7392 RTMemTmpFree(pRenameState->apszNewName);
7393 }
7394 if (pRenameState->apszNewLines)
7395 {
7396 for (unsigned i = 0; i < pRenameState->cExtents; i++)
7397 if (pRenameState->apszNewLines[i])
7398 RTStrFree(pRenameState->apszNewLines[i]);
7399 RTMemTmpFree(pRenameState->apszNewLines);
7400 }
7401 if (pRenameState->pszOldDescName)
7402 RTStrFree(pRenameState->pszOldDescName);
7403 if (pRenameState->pszOldBaseName)
7404 RTStrFree(pRenameState->pszOldBaseName);
7405 if (pRenameState->pszNewBaseName)
7406 RTStrFree(pRenameState->pszNewBaseName);
7407 if (pRenameState->pszOldFullName)
7408 RTStrFree(pRenameState->pszOldFullName);
7409 if (pRenameState->pszNewFullName)
7410 RTStrFree(pRenameState->pszNewFullName);
7411}
7412
7413/**
7414 * Rolls back the rename operation to the original state.
7415 *
7416 * @returns VBox status code.
7417 * @param pImage VMDK image instance.
7418 * @param pRenameState The rename state.
7419 */
7420static int vmdkRenameRollback(PVMDKIMAGE pImage, PVMDKRENAMESTATE pRenameState)
7421{
7422 int rc = VINF_SUCCESS;
7423
7424 if (!pRenameState->fImageFreed)
7425 {
7426 /*
7427 * Some extents may have been closed, close the rest. We will
7428 * re-open the whole thing later.
7429 */
7430 vmdkFreeImage(pImage, false, true /*fFlush*/);
7431 }
7432
7433 /* Rename files back. */
7434 for (unsigned i = 0; i <= pRenameState->cExtents; i++)
7435 {
7436 if (pRenameState->apszOldName[i])
7437 {
7438 rc = vdIfIoIntFileMove(pImage->pIfIo, pRenameState->apszNewName[i], pRenameState->apszOldName[i], 0);
7439 AssertRC(rc);
7440 }
7441 }
7442 /* Restore the old descriptor. */
7443 PVMDKFILE pFile;
7444 rc = vmdkFileOpen(pImage, &pFile, NULL, pRenameState->pszOldDescName,
7445 VDOpenFlagsToFileOpenFlags(VD_OPEN_FLAGS_NORMAL,
7446 false /* fCreate */));
7447 AssertRC(rc);
7448 if (pRenameState->fEmbeddedDesc)
7449 {
7450 pRenameState->ExtentCopy.pFile = pFile;
7451 pImage->pExtents = &pRenameState->ExtentCopy;
7452 }
7453 else
7454 {
7455 /* Shouldn't be null for separate descriptor.
7456 * There will be no access to the actual content.
7457 */
7458 pImage->pDescData = pRenameState->pszOldDescName;
7459 pImage->pFile = pFile;
7460 }
7461 pImage->Descriptor = pRenameState->DescriptorCopy;
7462 vmdkWriteDescriptor(pImage, NULL);
7463 vmdkFileClose(pImage, &pFile, false);
7464 /* Get rid of the stuff we implanted. */
7465 pImage->pExtents = NULL;
7466 pImage->pFile = NULL;
7467 pImage->pDescData = NULL;
7468 /* Re-open the image back. */
7469 pImage->pszFilename = pRenameState->pszOldImageName;
7470 rc = vmdkOpenImage(pImage, pImage->uOpenFlags);
7471
7472 return rc;
7473}
7474
7475/**
7476 * Rename worker doing the real work.
7477 *
7478 * @returns VBox status code.
7479 * @param pImage VMDK image instance.
7480 * @param pRenameState The rename state.
7481 * @param pszFilename The new filename.
7482 */
7483static int vmdkRenameWorker(PVMDKIMAGE pImage, PVMDKRENAMESTATE pRenameState, const char *pszFilename)
7484{
7485 int rc = VINF_SUCCESS;
7486 unsigned i, line;
7487
7488 /* Update the descriptor with modified extent names. */
7489 for (i = 0, line = pImage->Descriptor.uFirstExtent;
7490 i < pRenameState->cExtents;
7491 i++, line = pImage->Descriptor.aNextLines[line])
7492 {
7493 /* Update the descriptor. */
7494 pRenameState->apszNewLines[i] = vmdkStrReplace(pImage->Descriptor.aLines[line],
7495 pRenameState->pszOldBaseName,
7496 pRenameState->pszNewBaseName);
7497 if (!pRenameState->apszNewLines[i])
7498 {
7499 rc = VERR_NO_MEMORY;
7500 break;
7501 }
7502 pImage->Descriptor.aLines[line] = pRenameState->apszNewLines[i];
7503 }
7504
7505 if (RT_SUCCESS(rc))
7506 {
7507 /* Make sure the descriptor gets written back. */
7508 pImage->Descriptor.fDirty = true;
7509 /* Flush the descriptor now, in case it is embedded. */
7510 vmdkFlushImage(pImage, NULL);
7511
7512 /* Close and rename/move extents. */
7513 for (i = 0; i < pRenameState->cExtents; i++)
7514 {
7515 PVMDKEXTENT pExtent = &pImage->pExtents[i];
7516 /* Compose new name for the extent. */
7517 pRenameState->apszNewName[i] = vmdkStrReplace(pExtent->pszFullname,
7518 pRenameState->pszOldFullName,
7519 pRenameState->pszNewFullName);
7520 if (!pRenameState->apszNewName[i])
7521 {
7522 rc = VERR_NO_MEMORY;
7523 break;
7524 }
7525 /* Close the extent file. */
7526 rc = vmdkFileClose(pImage, &pExtent->pFile, false);
7527 if (RT_FAILURE(rc))
7528 break;;
7529
7530 /* Rename the extent file. */
7531 rc = vdIfIoIntFileMove(pImage->pIfIo, pExtent->pszFullname, pRenameState->apszNewName[i], 0);
7532 if (RT_FAILURE(rc))
7533 break;
7534 /* Remember the old name. */
7535 pRenameState->apszOldName[i] = RTStrDup(pExtent->pszFullname);
7536 }
7537
7538 if (RT_SUCCESS(rc))
7539 {
7540 /* Release all old stuff. */
7541 rc = vmdkFreeImage(pImage, false, true /*fFlush*/);
7542 if (RT_SUCCESS(rc))
7543 {
7544 pRenameState->fImageFreed = true;
7545
7546 /* Last elements of new/old name arrays are intended for
7547 * storing descriptor's names.
7548 */
7549 pRenameState->apszNewName[pRenameState->cExtents] = RTStrDup(pszFilename);
7550 /* Rename the descriptor file if it's separate. */
7551 if (!pRenameState->fEmbeddedDesc)
7552 {
7553 rc = vdIfIoIntFileMove(pImage->pIfIo, pImage->pszFilename, pRenameState->apszNewName[pRenameState->cExtents], 0);
7554 if (RT_SUCCESS(rc))
7555 {
7556 /* Save old name only if we may need to change it back. */
7557 pRenameState->apszOldName[pRenameState->cExtents] = RTStrDup(pszFilename);
7558 }
7559 }
7560
7561 /* Update pImage with the new information. */
7562 pImage->pszFilename = pszFilename;
7563
7564 /* Open the new image. */
7565 rc = vmdkOpenImage(pImage, pImage->uOpenFlags);
7566 }
7567 }
7568 }
7569
7570 return rc;
7571}
7572
7573/** @copydoc VDIMAGEBACKEND::pfnRename */
7574static DECLCALLBACK(int) vmdkRename(void *pBackendData, const char *pszFilename)
7575{
7576 LogFlowFunc(("pBackendData=%#p pszFilename=%#p\n", pBackendData, pszFilename));
7577
7578 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
7579 VMDKRENAMESTATE RenameState;
7580
7581 memset(&RenameState, 0, sizeof(RenameState));
7582
7583 /* Check arguments. */
7584 AssertPtrReturn(pImage, VERR_INVALID_POINTER);
7585 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
7586 AssertReturn(*pszFilename != '\0', VERR_INVALID_PARAMETER);
7587 AssertReturn(!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK), VERR_INVALID_PARAMETER);
7588
7589 int rc = vmdkRenameStatePrepare(pImage, &RenameState, pszFilename);
7590 if (RT_SUCCESS(rc))
7591 {
7592 /* --- Up to this point we have not done any damage yet. --- */
7593
7594 rc = vmdkRenameWorker(pImage, &RenameState, pszFilename);
7595 /* Roll back all changes in case of failure. */
7596 if (RT_FAILURE(rc))
7597 {
7598 int rrc = vmdkRenameRollback(pImage, &RenameState);
7599 AssertRC(rrc);
7600 }
7601 }
7602
7603 vmdkRenameStateDestroy(&RenameState);
7604 LogFlowFunc(("returns %Rrc\n", rc));
7605 return rc;
7606}
7607
7608/** @copydoc VDIMAGEBACKEND::pfnClose */
7609static DECLCALLBACK(int) vmdkClose(void *pBackendData, bool fDelete)
7610{
7611 LogFlowFunc(("pBackendData=%#p fDelete=%d\n", pBackendData, fDelete));
7612 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
7613
7614 int rc = vmdkFreeImage(pImage, fDelete, true /*fFlush*/);
7615 RTMemFree(pImage);
7616
7617 LogFlowFunc(("returns %Rrc\n", rc));
7618 return rc;
7619}
7620
7621/** @copydoc VDIMAGEBACKEND::pfnRead */
7622static DECLCALLBACK(int) vmdkRead(void *pBackendData, uint64_t uOffset, size_t cbToRead,
7623 PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
7624{
7625 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToRead=%zu pcbActuallyRead=%#p\n",
7626 pBackendData, uOffset, pIoCtx, cbToRead, pcbActuallyRead));
7627 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
7628
7629 AssertPtr(pImage);
7630 Assert(uOffset % 512 == 0);
7631 Assert(cbToRead % 512 == 0);
7632 AssertPtrReturn(pIoCtx, VERR_INVALID_POINTER);
7633 AssertReturn(cbToRead, VERR_INVALID_PARAMETER);
7634 AssertReturn(uOffset + cbToRead <= pImage->cbSize, VERR_INVALID_PARAMETER);
7635
7636 /* Find the extent and check access permissions as defined in the extent descriptor. */
7637 PVMDKEXTENT pExtent;
7638 uint64_t uSectorExtentRel;
7639 int rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
7640 &pExtent, &uSectorExtentRel);
7641 if ( RT_SUCCESS(rc)
7642 && pExtent->enmAccess != VMDKACCESS_NOACCESS)
7643 {
7644 /* Clip read range to remain in this extent. */
7645 cbToRead = RT_MIN(cbToRead, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
7646
7647 /* Handle the read according to the current extent type. */
7648 switch (pExtent->enmType)
7649 {
7650 case VMDKETYPE_HOSTED_SPARSE:
7651 {
7652 uint64_t uSectorExtentAbs;
7653
7654 rc = vmdkGetSector(pImage, pIoCtx, pExtent, uSectorExtentRel, &uSectorExtentAbs);
7655 if (RT_FAILURE(rc))
7656 break;
7657 /* Clip read range to at most the rest of the grain. */
7658 cbToRead = RT_MIN(cbToRead, VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain - uSectorExtentRel % pExtent->cSectorsPerGrain));
7659 Assert(!(cbToRead % 512));
7660 if (uSectorExtentAbs == 0)
7661 {
7662 if ( !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
7663 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY)
7664 || !(pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL))
7665 rc = VERR_VD_BLOCK_FREE;
7666 else
7667 rc = vmdkStreamReadSequential(pImage, pExtent,
7668 uSectorExtentRel,
7669 pIoCtx, cbToRead);
7670 }
7671 else
7672 {
7673 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
7674 {
7675 AssertMsg(vdIfIoIntIoCtxIsSynchronous(pImage->pIfIo, pIoCtx),
7676 ("Async I/O is not supported for stream optimized VMDK's\n"));
7677
7678 uint32_t uSectorInGrain = uSectorExtentRel % pExtent->cSectorsPerGrain;
7679 uSectorExtentAbs -= uSectorInGrain;
7680 if (pExtent->uGrainSectorAbs != uSectorExtentAbs)
7681 {
7682 uint64_t uLBA = 0; /* gcc maybe uninitialized */
7683 rc = vmdkFileInflateSync(pImage, pExtent,
7684 VMDK_SECTOR2BYTE(uSectorExtentAbs),
7685 pExtent->pvGrain,
7686 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain),
7687 NULL, &uLBA, NULL);
7688 if (RT_FAILURE(rc))
7689 {
7690 pExtent->uGrainSectorAbs = 0;
7691 break;
7692 }
7693 pExtent->uGrainSectorAbs = uSectorExtentAbs;
7694 pExtent->uGrain = uSectorExtentRel / pExtent->cSectorsPerGrain;
7695 Assert(uLBA == uSectorExtentRel);
7696 }
7697 vdIfIoIntIoCtxCopyTo(pImage->pIfIo, pIoCtx,
7698 (uint8_t *)pExtent->pvGrain
7699 + VMDK_SECTOR2BYTE(uSectorInGrain),
7700 cbToRead);
7701 }
7702 else
7703 rc = vdIfIoIntFileReadUser(pImage->pIfIo, pExtent->pFile->pStorage,
7704 VMDK_SECTOR2BYTE(uSectorExtentAbs),
7705 pIoCtx, cbToRead);
7706 }
7707 break;
7708 }
7709 case VMDKETYPE_VMFS:
7710 case VMDKETYPE_FLAT:
7711 rc = vdIfIoIntFileReadUser(pImage->pIfIo, pExtent->pFile->pStorage,
7712 VMDK_SECTOR2BYTE(uSectorExtentRel),
7713 pIoCtx, cbToRead);
7714 break;
7715 case VMDKETYPE_ZERO:
7716 {
7717 size_t cbSet;
7718
7719 cbSet = vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
7720 Assert(cbSet == cbToRead);
7721 break;
7722 }
7723 }
7724 if (pcbActuallyRead)
7725 *pcbActuallyRead = cbToRead;
7726 }
7727 else if (RT_SUCCESS(rc))
7728 rc = VERR_VD_VMDK_INVALID_STATE;
7729
7730 LogFlowFunc(("returns %Rrc\n", rc));
7731 return rc;
7732}
7733
7734/** @copydoc VDIMAGEBACKEND::pfnWrite */
7735static DECLCALLBACK(int) vmdkWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
7736 PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
7737 size_t *pcbPostRead, unsigned fWrite)
7738{
7739 LogFlowFunc(("pBackendData=%#p uOffset=%llu pIoCtx=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n",
7740 pBackendData, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
7741 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
7742 int rc;
7743
7744 AssertPtr(pImage);
7745 Assert(uOffset % 512 == 0);
7746 Assert(cbToWrite % 512 == 0);
7747 AssertPtrReturn(pIoCtx, VERR_INVALID_POINTER);
7748 AssertReturn(cbToWrite, VERR_INVALID_PARAMETER);
7749
7750 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
7751 {
7752 PVMDKEXTENT pExtent;
7753 uint64_t uSectorExtentRel;
7754 uint64_t uSectorExtentAbs;
7755
7756 /* No size check here, will do that later when the extent is located.
7757 * There are sparse images out there which according to the spec are
7758 * invalid, because the total size is not a multiple of the grain size.
7759 * Also for sparse images which are stitched together in odd ways (not at
7760 * grain boundaries, and with the nominal size not being a multiple of the
7761 * grain size), this would prevent writing to the last grain. */
7762
7763 rc = vmdkFindExtent(pImage, VMDK_BYTE2SECTOR(uOffset),
7764 &pExtent, &uSectorExtentRel);
7765 if (RT_SUCCESS(rc))
7766 {
7767 if ( pExtent->enmAccess != VMDKACCESS_READWRITE
7768 && ( !(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
7769 && !pImage->pExtents[0].uAppendPosition
7770 && pExtent->enmAccess != VMDKACCESS_READONLY))
7771 rc = VERR_VD_VMDK_INVALID_STATE;
7772 else
7773 {
7774 /* Handle the write according to the current extent type. */
7775 switch (pExtent->enmType)
7776 {
7777 case VMDKETYPE_HOSTED_SPARSE:
7778 rc = vmdkGetSector(pImage, pIoCtx, pExtent, uSectorExtentRel, &uSectorExtentAbs);
7779 if (RT_SUCCESS(rc))
7780 {
7781 if ( pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED
7782 && uSectorExtentRel < (uint64_t)pExtent->uLastGrainAccess * pExtent->cSectorsPerGrain)
7783 rc = VERR_VD_VMDK_INVALID_WRITE;
7784 else
7785 {
7786 /* Clip write range to at most the rest of the grain. */
7787 cbToWrite = RT_MIN(cbToWrite,
7788 VMDK_SECTOR2BYTE( pExtent->cSectorsPerGrain
7789 - uSectorExtentRel % pExtent->cSectorsPerGrain));
7790 if (uSectorExtentAbs == 0)
7791 {
7792 if (!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
7793 {
7794 if (cbToWrite == VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain))
7795 {
7796 /* Full block write to a previously unallocated block.
7797 * Check if the caller wants to avoid the automatic alloc. */
7798 if (!(fWrite & VD_WRITE_NO_ALLOC))
7799 {
7800 /* Allocate GT and find out where to store the grain. */
7801 rc = vmdkAllocGrain(pImage, pExtent, pIoCtx,
7802 uSectorExtentRel, cbToWrite);
7803 }
7804 else
7805 rc = VERR_VD_BLOCK_FREE;
7806 *pcbPreRead = 0;
7807 *pcbPostRead = 0;
7808 }
7809 else
7810 {
7811 /* Clip write range to remain in this extent. */
7812 cbToWrite = RT_MIN(cbToWrite,
7813 VMDK_SECTOR2BYTE( pExtent->uSectorOffset
7814 + pExtent->cNominalSectors - uSectorExtentRel));
7815 *pcbPreRead = VMDK_SECTOR2BYTE(uSectorExtentRel % pExtent->cSectorsPerGrain);
7816 *pcbPostRead = VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain) - cbToWrite - *pcbPreRead;
7817 rc = VERR_VD_BLOCK_FREE;
7818 }
7819 }
7820 else
7821 rc = vmdkStreamAllocGrain(pImage, pExtent, uSectorExtentRel,
7822 pIoCtx, cbToWrite);
7823 }
7824 else
7825 {
7826 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
7827 {
7828 /* A partial write to a streamOptimized image is simply
7829 * invalid. It requires rewriting already compressed data
7830 * which is somewhere between expensive and impossible. */
7831 rc = VERR_VD_VMDK_INVALID_STATE;
7832 pExtent->uGrainSectorAbs = 0;
7833 AssertRC(rc);
7834 }
7835 else
7836 {
7837 Assert(!(pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED));
7838 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pExtent->pFile->pStorage,
7839 VMDK_SECTOR2BYTE(uSectorExtentAbs),
7840 pIoCtx, cbToWrite, NULL, NULL);
7841 }
7842 }
7843 }
7844 }
7845 break;
7846 case VMDKETYPE_VMFS:
7847 case VMDKETYPE_FLAT:
7848 /* Clip write range to remain in this extent. */
7849 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
7850 rc = vdIfIoIntFileWriteUser(pImage->pIfIo, pExtent->pFile->pStorage,
7851 VMDK_SECTOR2BYTE(uSectorExtentRel),
7852 pIoCtx, cbToWrite, NULL, NULL);
7853 break;
7854 case VMDKETYPE_ZERO:
7855 /* Clip write range to remain in this extent. */
7856 cbToWrite = RT_MIN(cbToWrite, VMDK_SECTOR2BYTE(pExtent->uSectorOffset + pExtent->cNominalSectors - uSectorExtentRel));
7857 break;
7858 }
7859 }
7860
7861 if (pcbWriteProcess)
7862 *pcbWriteProcess = cbToWrite;
7863 }
7864 }
7865 else
7866 rc = VERR_VD_IMAGE_READ_ONLY;
7867
7868 LogFlowFunc(("returns %Rrc\n", rc));
7869 return rc;
7870}
7871
7872/** @copydoc VDIMAGEBACKEND::pfnFlush */
7873static DECLCALLBACK(int) vmdkFlush(void *pBackendData, PVDIOCTX pIoCtx)
7874{
7875 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
7876
7877 return vmdkFlushImage(pImage, pIoCtx);
7878}
7879
7880/** @copydoc VDIMAGEBACKEND::pfnGetVersion */
7881static DECLCALLBACK(unsigned) vmdkGetVersion(void *pBackendData)
7882{
7883 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
7884 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
7885
7886 AssertPtrReturn(pImage, 0);
7887
7888 return VMDK_IMAGE_VERSION;
7889}
7890
7891/** @copydoc VDIMAGEBACKEND::pfnGetFileSize */
7892static DECLCALLBACK(uint64_t) vmdkGetFileSize(void *pBackendData)
7893{
7894 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
7895 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
7896 uint64_t cb = 0;
7897
7898 AssertPtrReturn(pImage, 0);
7899
7900 if (pImage->pFile != NULL)
7901 {
7902 uint64_t cbFile;
7903 int rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pFile->pStorage, &cbFile);
7904 if (RT_SUCCESS(rc))
7905 cb += cbFile;
7906 }
7907 for (unsigned i = 0; i < pImage->cExtents; i++)
7908 {
7909 if (pImage->pExtents[i].pFile != NULL)
7910 {
7911 uint64_t cbFile;
7912 int rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pExtents[i].pFile->pStorage, &cbFile);
7913 if (RT_SUCCESS(rc))
7914 cb += cbFile;
7915 }
7916 }
7917
7918 LogFlowFunc(("returns %lld\n", cb));
7919 return cb;
7920}
7921
7922/** @copydoc VDIMAGEBACKEND::pfnGetPCHSGeometry */
7923static DECLCALLBACK(int) vmdkGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
7924{
7925 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p\n", pBackendData, pPCHSGeometry));
7926 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
7927 int rc = VINF_SUCCESS;
7928
7929 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
7930
7931 if (pImage->PCHSGeometry.cCylinders)
7932 *pPCHSGeometry = pImage->PCHSGeometry;
7933 else
7934 rc = VERR_VD_GEOMETRY_NOT_SET;
7935
7936 LogFlowFunc(("returns %Rrc (PCHS=%u/%u/%u)\n", rc, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
7937 return rc;
7938}
7939
7940/** @copydoc VDIMAGEBACKEND::pfnSetPCHSGeometry */
7941static DECLCALLBACK(int) vmdkSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
7942{
7943 LogFlowFunc(("pBackendData=%#p pPCHSGeometry=%#p PCHS=%u/%u/%u\n",
7944 pBackendData, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
7945 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
7946 int rc = VINF_SUCCESS;
7947
7948 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
7949
7950 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
7951 {
7952 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
7953 {
7954 rc = vmdkDescSetPCHSGeometry(pImage, pPCHSGeometry);
7955 if (RT_SUCCESS(rc))
7956 pImage->PCHSGeometry = *pPCHSGeometry;
7957 }
7958 else
7959 rc = VERR_NOT_SUPPORTED;
7960 }
7961 else
7962 rc = VERR_VD_IMAGE_READ_ONLY;
7963
7964 LogFlowFunc(("returns %Rrc\n", rc));
7965 return rc;
7966}
7967
7968/** @copydoc VDIMAGEBACKEND::pfnGetLCHSGeometry */
7969static DECLCALLBACK(int) vmdkGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
7970{
7971 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p\n", pBackendData, pLCHSGeometry));
7972 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
7973 int rc = VINF_SUCCESS;
7974
7975 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
7976
7977 if (pImage->LCHSGeometry.cCylinders)
7978 *pLCHSGeometry = pImage->LCHSGeometry;
7979 else
7980 rc = VERR_VD_GEOMETRY_NOT_SET;
7981
7982 LogFlowFunc(("returns %Rrc (LCHS=%u/%u/%u)\n", rc, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
7983 return rc;
7984}
7985
7986/** @copydoc VDIMAGEBACKEND::pfnSetLCHSGeometry */
7987static DECLCALLBACK(int) vmdkSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
7988{
7989 LogFlowFunc(("pBackendData=%#p pLCHSGeometry=%#p LCHS=%u/%u/%u\n",
7990 pBackendData, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
7991 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
7992 int rc = VINF_SUCCESS;
7993
7994 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
7995
7996 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
7997 {
7998 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
7999 {
8000 rc = vmdkDescSetLCHSGeometry(pImage, pLCHSGeometry);
8001 if (RT_SUCCESS(rc))
8002 pImage->LCHSGeometry = *pLCHSGeometry;
8003 }
8004 else
8005 rc = VERR_NOT_SUPPORTED;
8006 }
8007 else
8008 rc = VERR_VD_IMAGE_READ_ONLY;
8009
8010 LogFlowFunc(("returns %Rrc\n", rc));
8011 return rc;
8012}
8013
8014/** @copydoc VDIMAGEBACKEND::pfnQueryRegions */
8015static DECLCALLBACK(int) vmdkQueryRegions(void *pBackendData, PCVDREGIONLIST *ppRegionList)
8016{
8017 LogFlowFunc(("pBackendData=%#p ppRegionList=%#p\n", pBackendData, ppRegionList));
8018 PVMDKIMAGE pThis = (PVMDKIMAGE)pBackendData;
8019
8020 AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
8021
8022 *ppRegionList = &pThis->RegionList;
8023 LogFlowFunc(("returns %Rrc\n", VINF_SUCCESS));
8024 return VINF_SUCCESS;
8025}
8026
8027/** @copydoc VDIMAGEBACKEND::pfnRegionListRelease */
8028static DECLCALLBACK(void) vmdkRegionListRelease(void *pBackendData, PCVDREGIONLIST pRegionList)
8029{
8030 RT_NOREF1(pRegionList);
8031 LogFlowFunc(("pBackendData=%#p pRegionList=%#p\n", pBackendData, pRegionList));
8032 PVMDKIMAGE pThis = (PVMDKIMAGE)pBackendData;
8033 AssertPtr(pThis); RT_NOREF(pThis);
8034
8035 /* Nothing to do here. */
8036}
8037
8038/** @copydoc VDIMAGEBACKEND::pfnGetImageFlags */
8039static DECLCALLBACK(unsigned) vmdkGetImageFlags(void *pBackendData)
8040{
8041 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
8042 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
8043
8044 AssertPtrReturn(pImage, 0);
8045
8046 LogFlowFunc(("returns %#x\n", pImage->uImageFlags));
8047 return pImage->uImageFlags;
8048}
8049
8050/** @copydoc VDIMAGEBACKEND::pfnGetOpenFlags */
8051static DECLCALLBACK(unsigned) vmdkGetOpenFlags(void *pBackendData)
8052{
8053 LogFlowFunc(("pBackendData=%#p\n", pBackendData));
8054 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
8055
8056 AssertPtrReturn(pImage, 0);
8057
8058 LogFlowFunc(("returns %#x\n", pImage->uOpenFlags));
8059 return pImage->uOpenFlags;
8060}
8061
8062/** @copydoc VDIMAGEBACKEND::pfnSetOpenFlags */
8063static DECLCALLBACK(int) vmdkSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
8064{
8065 LogFlowFunc(("pBackendData=%#p uOpenFlags=%#x\n", pBackendData, uOpenFlags));
8066 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
8067 int rc;
8068
8069 /* Image must be opened and the new flags must be valid. */
8070 if (!pImage || (uOpenFlags & ~( VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
8071 | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
8072 | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS)))
8073 rc = VERR_INVALID_PARAMETER;
8074 else
8075 {
8076 /* StreamOptimized images need special treatment: reopen is prohibited. */
8077 if (pImage->uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)
8078 {
8079 if (pImage->uOpenFlags == uOpenFlags)
8080 rc = VINF_SUCCESS;
8081 else
8082 rc = VERR_INVALID_PARAMETER;
8083 }
8084 else
8085 {
8086 /* Implement this operation via reopening the image. */
8087 vmdkFreeImage(pImage, false, true /*fFlush*/);
8088 rc = vmdkOpenImage(pImage, uOpenFlags);
8089 }
8090 }
8091
8092 LogFlowFunc(("returns %Rrc\n", rc));
8093 return rc;
8094}
8095
8096/** @copydoc VDIMAGEBACKEND::pfnGetComment */
8097static DECLCALLBACK(int) vmdkGetComment(void *pBackendData, char *pszComment, size_t cbComment)
8098{
8099 LogFlowFunc(("pBackendData=%#p pszComment=%#p cbComment=%zu\n", pBackendData, pszComment, cbComment));
8100 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
8101
8102 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
8103
8104 char *pszCommentEncoded = NULL;
8105 int rc = vmdkDescDDBGetStr(pImage, &pImage->Descriptor,
8106 "ddb.comment", &pszCommentEncoded);
8107 if (rc == VERR_VD_VMDK_VALUE_NOT_FOUND)
8108 {
8109 pszCommentEncoded = NULL;
8110 rc = VINF_SUCCESS;
8111 }
8112
8113 if (RT_SUCCESS(rc))
8114 {
8115 if (pszComment && pszCommentEncoded)
8116 rc = vmdkDecodeString(pszCommentEncoded, pszComment, cbComment);
8117 else if (pszComment)
8118 *pszComment = '\0';
8119
8120 if (pszCommentEncoded)
8121 RTMemTmpFree(pszCommentEncoded);
8122 }
8123
8124 LogFlowFunc(("returns %Rrc comment='%s'\n", rc, pszComment));
8125 return rc;
8126}
8127
8128/** @copydoc VDIMAGEBACKEND::pfnSetComment */
8129static DECLCALLBACK(int) vmdkSetComment(void *pBackendData, const char *pszComment)
8130{
8131 LogFlowFunc(("pBackendData=%#p pszComment=\"%s\"\n", pBackendData, pszComment));
8132 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
8133 int rc;
8134
8135 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
8136
8137 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
8138 {
8139 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
8140 rc = vmdkSetImageComment(pImage, pszComment);
8141 else
8142 rc = VERR_NOT_SUPPORTED;
8143 }
8144 else
8145 rc = VERR_VD_IMAGE_READ_ONLY;
8146
8147 LogFlowFunc(("returns %Rrc\n", rc));
8148 return rc;
8149}
8150
8151/** @copydoc VDIMAGEBACKEND::pfnGetUuid */
8152static DECLCALLBACK(int) vmdkGetUuid(void *pBackendData, PRTUUID pUuid)
8153{
8154 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
8155 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
8156
8157 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
8158
8159 *pUuid = pImage->ImageUuid;
8160
8161 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VINF_SUCCESS, pUuid));
8162 return VINF_SUCCESS;
8163}
8164
8165/** @copydoc VDIMAGEBACKEND::pfnSetUuid */
8166static DECLCALLBACK(int) vmdkSetUuid(void *pBackendData, PCRTUUID pUuid)
8167{
8168 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
8169 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
8170 int rc = VINF_SUCCESS;
8171
8172 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
8173
8174 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
8175 {
8176 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
8177 {
8178 pImage->ImageUuid = *pUuid;
8179 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
8180 VMDK_DDB_IMAGE_UUID, pUuid);
8181 if (RT_FAILURE(rc))
8182 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
8183 N_("VMDK: error storing image UUID in descriptor in '%s'"), pImage->pszFilename);
8184 }
8185 else
8186 rc = VERR_NOT_SUPPORTED;
8187 }
8188 else
8189 rc = VERR_VD_IMAGE_READ_ONLY;
8190
8191 LogFlowFunc(("returns %Rrc\n", rc));
8192 return rc;
8193}
8194
8195/** @copydoc VDIMAGEBACKEND::pfnGetModificationUuid */
8196static DECLCALLBACK(int) vmdkGetModificationUuid(void *pBackendData, PRTUUID pUuid)
8197{
8198 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
8199 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
8200
8201 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
8202
8203 *pUuid = pImage->ModificationUuid;
8204
8205 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VINF_SUCCESS, pUuid));
8206 return VINF_SUCCESS;
8207}
8208
8209/** @copydoc VDIMAGEBACKEND::pfnSetModificationUuid */
8210static DECLCALLBACK(int) vmdkSetModificationUuid(void *pBackendData, PCRTUUID pUuid)
8211{
8212 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
8213 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
8214 int rc = VINF_SUCCESS;
8215
8216 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
8217
8218 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
8219 {
8220 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
8221 {
8222 /* Only touch the modification uuid if it changed. */
8223 if (RTUuidCompare(&pImage->ModificationUuid, pUuid))
8224 {
8225 pImage->ModificationUuid = *pUuid;
8226 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
8227 VMDK_DDB_MODIFICATION_UUID, pUuid);
8228 if (RT_FAILURE(rc))
8229 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error storing modification UUID in descriptor in '%s'"), pImage->pszFilename);
8230 }
8231 }
8232 else
8233 rc = VERR_NOT_SUPPORTED;
8234 }
8235 else
8236 rc = VERR_VD_IMAGE_READ_ONLY;
8237
8238 LogFlowFunc(("returns %Rrc\n", rc));
8239 return rc;
8240}
8241
8242/** @copydoc VDIMAGEBACKEND::pfnGetParentUuid */
8243static DECLCALLBACK(int) vmdkGetParentUuid(void *pBackendData, PRTUUID pUuid)
8244{
8245 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
8246 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
8247
8248 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
8249
8250 *pUuid = pImage->ParentUuid;
8251
8252 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VINF_SUCCESS, pUuid));
8253 return VINF_SUCCESS;
8254}
8255
8256/** @copydoc VDIMAGEBACKEND::pfnSetParentUuid */
8257static DECLCALLBACK(int) vmdkSetParentUuid(void *pBackendData, PCRTUUID pUuid)
8258{
8259 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
8260 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
8261 int rc = VINF_SUCCESS;
8262
8263 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
8264
8265 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
8266 {
8267 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
8268 {
8269 pImage->ParentUuid = *pUuid;
8270 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
8271 VMDK_DDB_PARENT_UUID, pUuid);
8272 if (RT_FAILURE(rc))
8273 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS,
8274 N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename);
8275 }
8276 else
8277 rc = VERR_NOT_SUPPORTED;
8278 }
8279 else
8280 rc = VERR_VD_IMAGE_READ_ONLY;
8281
8282 LogFlowFunc(("returns %Rrc\n", rc));
8283 return rc;
8284}
8285
8286/** @copydoc VDIMAGEBACKEND::pfnGetParentModificationUuid */
8287static DECLCALLBACK(int) vmdkGetParentModificationUuid(void *pBackendData, PRTUUID pUuid)
8288{
8289 LogFlowFunc(("pBackendData=%#p pUuid=%#p\n", pBackendData, pUuid));
8290 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
8291
8292 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
8293
8294 *pUuid = pImage->ParentModificationUuid;
8295
8296 LogFlowFunc(("returns %Rrc (%RTuuid)\n", VINF_SUCCESS, pUuid));
8297 return VINF_SUCCESS;
8298}
8299
8300/** @copydoc VDIMAGEBACKEND::pfnSetParentModificationUuid */
8301static DECLCALLBACK(int) vmdkSetParentModificationUuid(void *pBackendData, PCRTUUID pUuid)
8302{
8303 LogFlowFunc(("pBackendData=%#p Uuid=%RTuuid\n", pBackendData, pUuid));
8304 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
8305 int rc = VINF_SUCCESS;
8306
8307 AssertPtrReturn(pImage, VERR_VD_NOT_OPENED);
8308
8309 if (!(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY))
8310 {
8311 if (!(pImage->uOpenFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
8312 {
8313 pImage->ParentModificationUuid = *pUuid;
8314 rc = vmdkDescDDBSetUuid(pImage, &pImage->Descriptor,
8315 VMDK_DDB_PARENT_MODIFICATION_UUID, pUuid);
8316 if (RT_FAILURE(rc))
8317 rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: error storing parent image UUID in descriptor in '%s'"), pImage->pszFilename);
8318 }
8319 else
8320 rc = VERR_NOT_SUPPORTED;
8321 }
8322 else
8323 rc = VERR_VD_IMAGE_READ_ONLY;
8324
8325 LogFlowFunc(("returns %Rrc\n", rc));
8326 return rc;
8327}
8328
8329/** @copydoc VDIMAGEBACKEND::pfnDump */
8330static DECLCALLBACK(void) vmdkDump(void *pBackendData)
8331{
8332 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
8333
8334 AssertPtrReturnVoid(pImage);
8335 vdIfErrorMessage(pImage->pIfError, "Header: Geometry PCHS=%u/%u/%u LCHS=%u/%u/%u cbSector=%llu\n",
8336 pImage->PCHSGeometry.cCylinders, pImage->PCHSGeometry.cHeads, pImage->PCHSGeometry.cSectors,
8337 pImage->LCHSGeometry.cCylinders, pImage->LCHSGeometry.cHeads, pImage->LCHSGeometry.cSectors,
8338 VMDK_BYTE2SECTOR(pImage->cbSize));
8339 vdIfErrorMessage(pImage->pIfError, "Header: uuidCreation={%RTuuid}\n", &pImage->ImageUuid);
8340 vdIfErrorMessage(pImage->pIfError, "Header: uuidModification={%RTuuid}\n", &pImage->ModificationUuid);
8341 vdIfErrorMessage(pImage->pIfError, "Header: uuidParent={%RTuuid}\n", &pImage->ParentUuid);
8342 vdIfErrorMessage(pImage->pIfError, "Header: uuidParentModification={%RTuuid}\n", &pImage->ParentModificationUuid);
8343}
8344
8345
8346/**
8347 * Returns the size, in bytes, of the sparse extent overhead for
8348 * the number of desired total sectors and based on the current
8349 * sectors of the extent.
8350 *
8351 * @returns uint64_t size of new overhead in bytes.
8352 * @param pExtent VMDK extent instance.
8353 * @param cSectorsNew Number of desired total sectors.
8354 */
8355static uint64_t vmdkGetNewOverhead(PVMDKEXTENT pExtent, uint64_t cSectorsNew)
8356{
8357 uint64_t cNewDirEntries = cSectorsNew / pExtent->cSectorsPerGDE;
8358 if (cSectorsNew % pExtent->cSectorsPerGDE)
8359 cNewDirEntries++;
8360
8361 size_t cbNewGD = cNewDirEntries * sizeof(uint32_t);
8362 uint64_t cbNewDirSize = RT_ALIGN_64(cbNewGD, 512);
8363 uint64_t cbNewAllTablesSize = RT_ALIGN_64(cNewDirEntries * pExtent->cGTEntries * sizeof(uint32_t), 512);
8364 uint64_t cbNewOverhead = RT_ALIGN_Z(RT_MAX(pExtent->uDescriptorSector
8365 + pExtent->cDescriptorSectors, 1)
8366 + cbNewDirSize + cbNewAllTablesSize, 512);
8367 cbNewOverhead += cbNewDirSize + cbNewAllTablesSize;
8368 cbNewOverhead = RT_ALIGN_64(cbNewOverhead,
8369 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
8370
8371 return cbNewOverhead;
8372}
8373
8374/**
8375 * Internal: Replaces the size (in sectors) of an extent in the descriptor file.
8376 *
8377 * @returns VBox status code.
8378 * @param pImage VMDK image instance.
8379 * @param pExtent VMDK extent instance.
8380 * @param uLine Line number of descriptor to change.
8381 * @param cSectorsOld Existing number of sectors.
8382 * @param cSectorsNew New number of sectors.
8383 */
8384static int vmdkReplaceExtentSize(PVMDKIMAGE pImage, PVMDKEXTENT pExtent, unsigned uLine, uint64_t cSectorsOld,
8385 uint64_t cSectorsNew)
8386{
8387 char szOldExtentSectors[UINT64_MAX_BUFF_SIZE];
8388 char szNewExtentSectors[UINT64_MAX_BUFF_SIZE];
8389
8390 ssize_t cbWritten = RTStrPrintf2(szOldExtentSectors, sizeof(szOldExtentSectors), "%llu", cSectorsOld);
8391 if (cbWritten <= 0 || cbWritten > (ssize_t)sizeof(szOldExtentSectors))
8392 return VERR_BUFFER_OVERFLOW;
8393
8394 cbWritten = RTStrPrintf2(szNewExtentSectors, sizeof(szNewExtentSectors), "%llu", cSectorsNew);
8395 if (cbWritten <= 0 || cbWritten > (ssize_t)sizeof(szNewExtentSectors))
8396 return VERR_BUFFER_OVERFLOW;
8397
8398 char *pszNewExtentLine = vmdkStrReplace(pImage->Descriptor.aLines[uLine],
8399 szOldExtentSectors,
8400 szNewExtentSectors);
8401
8402 if (RT_UNLIKELY(!pszNewExtentLine))
8403 return VERR_INVALID_PARAMETER;
8404
8405 vmdkDescExtRemoveByLine(pImage, &pImage->Descriptor, uLine);
8406 vmdkDescExtInsert(pImage, &pImage->Descriptor,
8407 pExtent->enmAccess, cSectorsNew,
8408 pExtent->enmType, pExtent->pszBasename, pExtent->uSectorOffset);
8409
8410 RTStrFree(pszNewExtentLine);
8411 pszNewExtentLine = NULL;
8412
8413 pImage->Descriptor.fDirty = true;
8414
8415 return VINF_SUCCESS;
8416}
8417
8418/**
8419 * Moves sectors down to make room for new overhead.
8420 * Used for sparse extent resize.
8421 *
8422 * @returns VBox status code.
8423 * @param pImage VMDK image instance.
8424 * @param pExtent VMDK extent instance.
8425 * @param cSectorsNew Number of sectors after resize.
8426 */
8427static int vmdkRelocateSectorsForSparseResize(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
8428 uint64_t cSectorsNew)
8429{
8430 int rc = VINF_SUCCESS;
8431
8432 uint64_t cbNewOverhead = vmdkGetNewOverhead(pExtent, cSectorsNew);
8433
8434 uint64_t cNewOverheadSectors = VMDK_BYTE2SECTOR(cbNewOverhead);
8435 uint64_t cOverheadSectorDiff = cNewOverheadSectors - pExtent->cOverheadSectors;
8436
8437 uint64_t cbFile = 0;
8438 rc = vdIfIoIntFileGetSize(pImage->pIfIo, pExtent->pFile->pStorage, &cbFile);
8439
8440 uint64_t uNewAppendPosition;
8441
8442 /* Calculate how many sectors need to be relocated. */
8443 unsigned cSectorsReloc = cOverheadSectorDiff;
8444 if (cbNewOverhead % VMDK_SECTOR_SIZE)
8445 cSectorsReloc++;
8446
8447 if (cSectorsReloc < pExtent->cSectors)
8448 uNewAppendPosition = RT_ALIGN_Z(cbFile + VMDK_SECTOR2BYTE(cOverheadSectorDiff), 512);
8449 else
8450 uNewAppendPosition = cbFile;
8451
8452 /*
8453 * Get the blocks we need to relocate first, they are appended to the end
8454 * of the image.
8455 */
8456 void *pvBuf = NULL, *pvZero = NULL;
8457 do
8458 {
8459 /* Allocate data buffer. */
8460 pvBuf = RTMemAllocZ(VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
8461 if (!pvBuf)
8462 {
8463 rc = VERR_NO_MEMORY;
8464 break;
8465 }
8466
8467 /* Allocate buffer for overwriting with zeroes. */
8468 pvZero = RTMemAllocZ(VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
8469 if (!pvZero)
8470 {
8471 RTMemFree(pvBuf);
8472 pvBuf = NULL;
8473
8474 rc = VERR_NO_MEMORY;
8475 break;
8476 }
8477
8478 uint32_t *aGTDataTmp = (uint32_t *)RTMemAllocZ(sizeof(uint32_t) * pExtent->cGTEntries);
8479 if(!aGTDataTmp)
8480 {
8481 RTMemFree(pvBuf);
8482 pvBuf = NULL;
8483
8484 RTMemFree(pvZero);
8485 pvZero = NULL;
8486
8487 rc = VERR_NO_MEMORY;
8488 break;
8489 }
8490
8491 uint32_t *aRGTDataTmp = (uint32_t *)RTMemAllocZ(sizeof(uint32_t) * pExtent->cGTEntries);
8492 if(!aRGTDataTmp)
8493 {
8494 RTMemFree(pvBuf);
8495 pvBuf = NULL;
8496
8497 RTMemFree(pvZero);
8498 pvZero = NULL;
8499
8500 RTMemFree(aGTDataTmp);
8501 aGTDataTmp = NULL;
8502
8503 rc = VERR_NO_MEMORY;
8504 break;
8505 }
8506
8507 /* Search for overlap sector in the grain table. */
8508 for (uint32_t idxGD = 0; idxGD < pExtent->cGDEntries; idxGD++)
8509 {
8510 uint64_t uGTSector = pExtent->pGD[idxGD];
8511 uint64_t uRGTSector = pExtent->pRGD[idxGD];
8512
8513 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
8514 VMDK_SECTOR2BYTE(uGTSector),
8515 aGTDataTmp, sizeof(uint32_t) * pExtent->cGTEntries);
8516
8517 if (RT_FAILURE(rc))
8518 break;
8519
8520 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
8521 VMDK_SECTOR2BYTE(uRGTSector),
8522 aRGTDataTmp, sizeof(uint32_t) * pExtent->cGTEntries);
8523
8524 if (RT_FAILURE(rc))
8525 break;
8526
8527 for (uint32_t idxGT = 0; idxGT < pExtent->cGTEntries; idxGT++)
8528 {
8529 uint64_t aGTEntryLE = RT_LE2H_U64(aGTDataTmp[idxGT]);
8530 uint64_t aRGTEntryLE = RT_LE2H_U64(aRGTDataTmp[idxGT]);
8531
8532 /**
8533 * Check if grain table is valid. If not dump out with an error.
8534 * Shoudln't ever get here (given other checks) but good sanity check.
8535 */
8536 if (aGTEntryLE != aRGTEntryLE)
8537 {
8538 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
8539 N_("VMDK: inconsistent references within grain table in '%s'"), pExtent->pszFullname);
8540 break;
8541 }
8542
8543 if (aGTEntryLE < cNewOverheadSectors
8544 && aGTEntryLE != 0)
8545 {
8546 /* Read data and append grain to the end of the image. */
8547 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
8548 VMDK_SECTOR2BYTE(aGTEntryLE), pvBuf,
8549 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
8550 if (RT_FAILURE(rc))
8551 break;
8552
8553 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
8554 uNewAppendPosition, pvBuf,
8555 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
8556 if (RT_FAILURE(rc))
8557 break;
8558
8559 /* Zero out the old block area. */
8560 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
8561 VMDK_SECTOR2BYTE(aGTEntryLE), pvZero,
8562 VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain));
8563 if (RT_FAILURE(rc))
8564 break;
8565
8566 /* Write updated grain tables to file */
8567 aGTDataTmp[idxGT] = VMDK_BYTE2SECTOR(uNewAppendPosition);
8568 aRGTDataTmp[idxGT] = VMDK_BYTE2SECTOR(uNewAppendPosition);
8569
8570 if (memcmp(aGTDataTmp, aRGTDataTmp, sizeof(uint32_t) * pExtent->cGTEntries))
8571 {
8572 rc = vdIfError(pImage->pIfError, VERR_VD_VMDK_INVALID_HEADER, RT_SRC_POS,
8573 N_("VMDK: inconsistency between grain table and backup grain table in '%s'"), pExtent->pszFullname);
8574 break;
8575 }
8576
8577 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
8578 VMDK_SECTOR2BYTE(uGTSector),
8579 aGTDataTmp, sizeof(uint32_t) * pExtent->cGTEntries);
8580
8581 if (RT_FAILURE(rc))
8582 break;
8583
8584 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
8585 VMDK_SECTOR2BYTE(uRGTSector),
8586 aRGTDataTmp, sizeof(uint32_t) * pExtent->cGTEntries);
8587
8588 break;
8589 }
8590 }
8591 }
8592
8593 RTMemFree(aGTDataTmp);
8594 aGTDataTmp = NULL;
8595
8596 RTMemFree(aRGTDataTmp);
8597 aRGTDataTmp = NULL;
8598
8599 if (RT_FAILURE(rc))
8600 break;
8601
8602 uNewAppendPosition += VMDK_SECTOR2BYTE(pExtent->cSectorsPerGrain);
8603 } while (0);
8604
8605 if (pvBuf)
8606 {
8607 RTMemFree(pvBuf);
8608 pvBuf = NULL;
8609 }
8610
8611 if (pvZero)
8612 {
8613 RTMemFree(pvZero);
8614 pvZero = NULL;
8615 }
8616
8617 // Update append position for extent
8618 pExtent->uAppendPosition = uNewAppendPosition;
8619
8620 return rc;
8621}
8622
8623/**
8624 * Resizes meta/overhead for sparse extent resize.
8625 *
8626 * @returns VBox status code.
8627 * @param pImage VMDK image instance.
8628 * @param pExtent VMDK extent instance.
8629 * @param cSectorsNew Number of sectors after resize.
8630 */
8631static int vmdkResizeSparseMeta(PVMDKIMAGE pImage, PVMDKEXTENT pExtent,
8632 uint64_t cSectorsNew)
8633{
8634 int rc = VINF_SUCCESS;
8635 uint32_t cOldGDEntries = pExtent->cGDEntries;
8636
8637 uint64_t cNewDirEntries = cSectorsNew / pExtent->cSectorsPerGDE;
8638 if (cSectorsNew % pExtent->cSectorsPerGDE)
8639 cNewDirEntries++;
8640
8641 size_t cbNewGD = cNewDirEntries * sizeof(uint32_t);
8642
8643 uint64_t cbNewDirSize = RT_ALIGN_64(cbNewGD, 512);
8644 uint64_t cbCurrDirSize = RT_ALIGN_64(pExtent->cGDEntries * VMDK_GRAIN_DIR_ENTRY_SIZE, 512);
8645 uint64_t cDirSectorDiff = VMDK_BYTE2SECTOR(cbNewDirSize - cbCurrDirSize);
8646
8647 uint64_t cbNewAllTablesSize = RT_ALIGN_64(cNewDirEntries * pExtent->cGTEntries * sizeof(uint32_t), 512);
8648 uint64_t cbCurrAllTablesSize = RT_ALIGN_64(pExtent->cGDEntries * VMDK_GRAIN_TABLE_SIZE, 512);
8649 uint64_t cTableSectorDiff = VMDK_BYTE2SECTOR(cbNewAllTablesSize - cbCurrAllTablesSize);
8650
8651 uint64_t cbNewOverhead = vmdkGetNewOverhead(pExtent, cSectorsNew);
8652 uint64_t cNewOverheadSectors = VMDK_BYTE2SECTOR(cbNewOverhead);
8653 uint64_t cOverheadSectorDiff = cNewOverheadSectors - pExtent->cOverheadSectors;
8654
8655 /*
8656 * Get the blocks we need to relocate first, they are appended to the end
8657 * of the image.
8658 */
8659 void *pvBuf = NULL, *pvZero = NULL;
8660
8661 do
8662 {
8663 /* Allocate data buffer. */
8664 pvBuf = RTMemAllocZ(VMDK_GRAIN_TABLE_SIZE);
8665 if (!pvBuf)
8666 {
8667 rc = VERR_NO_MEMORY;
8668 break;
8669 }
8670
8671 /* Allocate buffer for overwriting with zeroes. */
8672 pvZero = RTMemAllocZ(VMDK_GRAIN_TABLE_SIZE);
8673 if (!pvZero)
8674 {
8675 RTMemFree(pvBuf);
8676 pvBuf = NULL;
8677
8678 rc = VERR_NO_MEMORY;
8679 break;
8680 }
8681
8682 uint32_t uGTStart = VMDK_SECTOR2BYTE(pExtent->uSectorGD) + (cOldGDEntries * VMDK_GRAIN_DIR_ENTRY_SIZE);
8683
8684 // points to last element in the grain table
8685 uint32_t uGTTail = uGTStart + (pExtent->cGDEntries * VMDK_GRAIN_TABLE_SIZE) - VMDK_GRAIN_TABLE_SIZE;
8686 uint32_t cbGTOff = RT_ALIGN_Z(VMDK_SECTOR2BYTE(cDirSectorDiff + cTableSectorDiff + cDirSectorDiff), 512);
8687
8688 for (int i = pExtent->cGDEntries - 1; i >= 0; i--)
8689 {
8690 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
8691 uGTTail, pvBuf,
8692 VMDK_GRAIN_TABLE_SIZE);
8693 if (RT_FAILURE(rc))
8694 break;
8695
8696 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
8697 RT_ALIGN_Z(uGTTail + cbGTOff, 512), pvBuf,
8698 VMDK_GRAIN_TABLE_SIZE);
8699 if (RT_FAILURE(rc))
8700 break;
8701
8702 // This overshoots when i == 0, but we don't need it anymore.
8703 uGTTail -= VMDK_GRAIN_TABLE_SIZE;
8704 }
8705
8706
8707 /* Find the end of the grain directory and start bumping everything down. Update locations of GT entries. */
8708 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
8709 VMDK_SECTOR2BYTE(pExtent->uSectorGD), pvBuf,
8710 pExtent->cGDEntries * VMDK_GRAIN_DIR_ENTRY_SIZE);
8711 if (RT_FAILURE(rc))
8712 break;
8713
8714 int * tmpBuf = (int *)pvBuf;
8715
8716 for (uint32_t i = 0; i < pExtent->cGDEntries; i++)
8717 {
8718 tmpBuf[i] = tmpBuf[i] + VMDK_BYTE2SECTOR(cbGTOff);
8719 pExtent->pGD[i] = pExtent->pGD[i] + VMDK_BYTE2SECTOR(cbGTOff);
8720 }
8721
8722 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
8723 RT_ALIGN_Z(VMDK_SECTOR2BYTE(pExtent->uSectorGD + cTableSectorDiff + cDirSectorDiff), 512), pvBuf,
8724 pExtent->cGDEntries * VMDK_GRAIN_DIR_ENTRY_SIZE);
8725 if (RT_FAILURE(rc))
8726 break;
8727
8728 pExtent->uSectorGD = pExtent->uSectorGD + cDirSectorDiff + cTableSectorDiff;
8729
8730 /* Repeat both steps with the redundant grain table/directory. */
8731
8732 uint32_t uRGTStart = VMDK_SECTOR2BYTE(pExtent->uSectorRGD) + (cOldGDEntries * VMDK_GRAIN_DIR_ENTRY_SIZE);
8733
8734 // points to last element in the grain table
8735 uint32_t uRGTTail = uRGTStart + (pExtent->cGDEntries * VMDK_GRAIN_TABLE_SIZE) - VMDK_GRAIN_TABLE_SIZE;
8736 uint32_t cbRGTOff = RT_ALIGN_Z(VMDK_SECTOR2BYTE(cDirSectorDiff), 512);
8737
8738 for (int i = pExtent->cGDEntries - 1; i >= 0; i--)
8739 {
8740 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
8741 uRGTTail, pvBuf,
8742 VMDK_GRAIN_TABLE_SIZE);
8743 if (RT_FAILURE(rc))
8744 break;
8745
8746 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
8747 RT_ALIGN_Z(uRGTTail + cbRGTOff, 512), pvBuf,
8748 VMDK_GRAIN_TABLE_SIZE);
8749 if (RT_FAILURE(rc))
8750 break;
8751
8752 // This overshoots when i == 0, but we don't need it anymore.
8753 uRGTTail -= VMDK_GRAIN_TABLE_SIZE;
8754 }
8755
8756 /* Update locations of GT entries. */
8757 rc = vdIfIoIntFileReadSync(pImage->pIfIo, pExtent->pFile->pStorage,
8758 VMDK_SECTOR2BYTE(pExtent->uSectorRGD), pvBuf,
8759 pExtent->cGDEntries * VMDK_GRAIN_DIR_ENTRY_SIZE);
8760 if (RT_FAILURE(rc))
8761 break;
8762
8763 tmpBuf = (int *)pvBuf;
8764
8765 for (uint32_t i = 0; i < pExtent->cGDEntries; i++)
8766 {
8767 tmpBuf[i] = tmpBuf[i] + cDirSectorDiff;
8768 pExtent->pRGD[i] = pExtent->pRGD[i] + cDirSectorDiff;
8769 }
8770
8771 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
8772 VMDK_SECTOR2BYTE(pExtent->uSectorRGD), pvBuf,
8773 pExtent->cGDEntries * VMDK_GRAIN_DIR_ENTRY_SIZE);
8774 if (RT_FAILURE(rc))
8775 break;
8776
8777 pExtent->uSectorRGD = pExtent->uSectorRGD;
8778 pExtent->cOverheadSectors += cOverheadSectorDiff;
8779
8780 } while (0);
8781
8782 if (pvBuf)
8783 {
8784 RTMemFree(pvBuf);
8785 pvBuf = NULL;
8786 }
8787
8788 if (pvZero)
8789 {
8790 RTMemFree(pvZero);
8791 pvZero = NULL;
8792 }
8793
8794 pExtent->cGDEntries = cNewDirEntries;
8795
8796 /* Allocate buffer for overwriting with zeroes. */
8797 pvZero = RTMemAllocZ(VMDK_GRAIN_TABLE_SIZE);
8798 if (!pvZero)
8799 return VERR_NO_MEMORY;
8800
8801 // Allocate additional grain dir
8802 pExtent->pGD = (uint32_t *) RTMemReallocZ(pExtent->pGD, pExtent->cGDEntries * sizeof(uint32_t), cbNewGD);
8803 if (RT_LIKELY(pExtent->pGD))
8804 {
8805 if (pExtent->uSectorRGD)
8806 {
8807 pExtent->pRGD = (uint32_t *)RTMemReallocZ(pExtent->pRGD, pExtent->cGDEntries * sizeof(uint32_t), cbNewGD);
8808 if (RT_UNLIKELY(!pExtent->pRGD))
8809 rc = VERR_NO_MEMORY;
8810 }
8811 }
8812 else
8813 return VERR_NO_MEMORY;
8814
8815
8816 uint32_t uTmpDirVal = pExtent->pGD[cOldGDEntries - 1] + VMDK_GRAIN_DIR_ENTRY_SIZE;
8817 for (uint32_t i = cOldGDEntries; i < pExtent->cGDEntries; i++)
8818 {
8819 pExtent->pGD[i] = uTmpDirVal;
8820
8821 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
8822 VMDK_SECTOR2BYTE(uTmpDirVal), pvZero,
8823 VMDK_GRAIN_TABLE_SIZE);
8824
8825 if (RT_FAILURE(rc))
8826 return rc;
8827
8828 uTmpDirVal += VMDK_GRAIN_DIR_ENTRY_SIZE;
8829 }
8830
8831 uint32_t uRTmpDirVal = pExtent->pRGD[cOldGDEntries - 1] + VMDK_GRAIN_DIR_ENTRY_SIZE;
8832 for (uint32_t i = cOldGDEntries; i < pExtent->cGDEntries; i++)
8833 {
8834 pExtent->pRGD[i] = uRTmpDirVal;
8835
8836 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
8837 VMDK_SECTOR2BYTE(uRTmpDirVal), pvZero,
8838 VMDK_GRAIN_TABLE_SIZE);
8839
8840 if (RT_FAILURE(rc))
8841 return rc;
8842
8843 uRTmpDirVal += VMDK_GRAIN_DIR_ENTRY_SIZE;
8844 }
8845
8846 RTMemFree(pvZero);
8847 pvZero = NULL;
8848
8849 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
8850 VMDK_SECTOR2BYTE(pExtent->uSectorGD), pExtent->pGD,
8851 pExtent->cGDEntries * VMDK_GRAIN_DIR_ENTRY_SIZE);
8852 if (RT_FAILURE(rc))
8853 return rc;
8854
8855 rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pExtent->pFile->pStorage,
8856 VMDK_SECTOR2BYTE(pExtent->uSectorRGD), pExtent->pRGD,
8857 pExtent->cGDEntries * VMDK_GRAIN_DIR_ENTRY_SIZE);
8858 if (RT_FAILURE(rc))
8859 return rc;
8860
8861 rc = vmdkReplaceExtentSize(pImage, pExtent, pImage->Descriptor.uFirstExtent + pExtent->uExtent,
8862 pExtent->cNominalSectors, cSectorsNew);
8863 if (RT_FAILURE(rc))
8864 return rc;
8865
8866 return rc;
8867}
8868
8869/** @copydoc VDIMAGEBACKEND::pfnResize */
8870static DECLCALLBACK(int) vmdkResize(void *pBackendData, uint64_t cbSize,
8871 PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry,
8872 unsigned uPercentStart, unsigned uPercentSpan,
8873 PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
8874 PVDINTERFACE pVDIfsOperation)
8875{
8876 RT_NOREF5(uPercentStart, uPercentSpan, pVDIfsDisk, pVDIfsImage, pVDIfsOperation);
8877
8878 // Establish variables and objects needed
8879 int rc = VINF_SUCCESS;
8880 PVMDKIMAGE pImage = (PVMDKIMAGE)pBackendData;
8881 unsigned uImageFlags = pImage->uImageFlags;
8882 PVMDKEXTENT pExtent = &pImage->pExtents[0];
8883 pExtent->fMetaDirty = true;
8884
8885 uint64_t cSectorsNew = cbSize / VMDK_SECTOR_SIZE; /** < New number of sectors in the image after the resize */
8886 if (cbSize % VMDK_SECTOR_SIZE)
8887 cSectorsNew++;
8888
8889 uint64_t cSectorsOld = pImage->cbSize / VMDK_SECTOR_SIZE; /** < Number of sectors before the resize. Only for FLAT images. */
8890 if (pImage->cbSize % VMDK_SECTOR_SIZE)
8891 cSectorsOld++;
8892 unsigned cExtents = pImage->cExtents;
8893
8894 /* Check size is within min/max bounds. */
8895 if ( !(uImageFlags & VD_VMDK_IMAGE_FLAGS_RAWDISK)
8896 && ( !cbSize
8897 || (!(uImageFlags & VD_IMAGE_FLAGS_FIXED) && cbSize >= _1T * 256 - _64K)) )
8898 return VERR_VD_INVALID_SIZE;
8899
8900 /*
8901 * Making the image smaller is not supported at the moment.
8902 */
8903 /** @todo implement making the image smaller, it is the responsibility of
8904 * the user to know what they're doing. */
8905 if (cbSize < pImage->cbSize)
8906 rc = VERR_VD_SHRINK_NOT_SUPPORTED;
8907 else if (cbSize > pImage->cbSize)
8908 {
8909 /**
8910 * monolithicFlat. FIXED flag and not split up into 2 GB parts.
8911 */
8912 if ((uImageFlags & VD_IMAGE_FLAGS_FIXED) && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G))
8913 {
8914 /** Required space in bytes for the extent after the resize. */
8915 uint64_t cbSectorSpaceNew = cSectorsNew * VMDK_SECTOR_SIZE;
8916 pExtent = &pImage->pExtents[0];
8917
8918 rc = vdIfIoIntFileSetAllocationSize(pImage->pIfIo, pExtent->pFile->pStorage, cbSectorSpaceNew,
8919 0 /* fFlags */, NULL,
8920 uPercentStart, uPercentSpan);
8921 if (RT_FAILURE(rc))
8922 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set size of new file '%s'"), pExtent->pszFullname);
8923
8924 rc = vmdkReplaceExtentSize(pImage, pExtent, pImage->Descriptor.uFirstExtent, cSectorsOld, cSectorsNew);
8925 if (RT_FAILURE(rc))
8926 return rc;
8927 }
8928
8929 /**
8930 * twoGbMaxExtentFlat. FIXED flag and SPLIT into 2 GB parts.
8931 */
8932 if ((uImageFlags & VD_IMAGE_FLAGS_FIXED) && (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G))
8933 {
8934 /* Check to see how much space remains in last extent */
8935 bool fSpaceAvailible = false;
8936 uint64_t cLastExtentRemSectors = cSectorsOld % VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE);
8937 if (cLastExtentRemSectors)
8938 fSpaceAvailible = true;
8939
8940 uint64_t cSectorsNeeded = cSectorsNew - cSectorsOld;
8941
8942 /** Space remaining in current last extent file that we don't need to create another one. */
8943 if (fSpaceAvailible && cSectorsNeeded + cLastExtentRemSectors <= VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE))
8944 {
8945 pExtent = &pImage->pExtents[cExtents - 1];
8946 rc = vdIfIoIntFileSetAllocationSize(pImage->pIfIo, pExtent->pFile->pStorage,
8947 VMDK_SECTOR2BYTE(cSectorsNeeded + cLastExtentRemSectors),
8948 0 /* fFlags */, NULL, uPercentStart, uPercentSpan);
8949 if (RT_FAILURE(rc))
8950 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set size of new file '%s'"), pExtent->pszFullname);
8951
8952 rc = vmdkReplaceExtentSize(pImage, pExtent, pImage->Descriptor.uFirstExtent + cExtents - 1,
8953 pExtent->cNominalSectors, cSectorsNeeded + cLastExtentRemSectors);
8954 if (RT_FAILURE(rc))
8955 return rc;
8956 }
8957 //** Need more extent files to handle all the requested space. */
8958 else
8959 {
8960 if (fSpaceAvailible)
8961 {
8962 pExtent = &pImage->pExtents[cExtents - 1];
8963 rc = vdIfIoIntFileSetAllocationSize(pImage->pIfIo, pExtent->pFile->pStorage, VMDK_2G_SPLIT_SIZE,
8964 0 /* fFlags */, NULL,
8965 uPercentStart, uPercentSpan);
8966 if (RT_FAILURE(rc))
8967 return vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VMDK: could not set size of new file '%s'"), pExtent->pszFullname);
8968
8969 cSectorsNeeded = cSectorsNeeded - VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE) + cLastExtentRemSectors;
8970
8971 rc = vmdkReplaceExtentSize(pImage, pExtent, pImage->Descriptor.uFirstExtent + cExtents - 1,
8972 pExtent->cNominalSectors, VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE));
8973 if (RT_FAILURE(rc))
8974 return rc;
8975 }
8976
8977 unsigned cNewExtents = VMDK_SECTOR2BYTE(cSectorsNeeded) / VMDK_2G_SPLIT_SIZE;
8978 if (cNewExtents % VMDK_2G_SPLIT_SIZE || cNewExtents < VMDK_2G_SPLIT_SIZE)
8979 cNewExtents++;
8980
8981 for (unsigned i = cExtents;
8982 i < cExtents + cNewExtents && cSectorsNeeded >= VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE);
8983 i++)
8984 {
8985 rc = vmdkAddFileBackedExtent(pImage, VMDK_2G_SPLIT_SIZE);
8986 if (RT_FAILURE(rc))
8987 return rc;
8988
8989 pExtent = &pImage->pExtents[i];
8990
8991 pExtent->cSectors = VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE);
8992 cSectorsNeeded -= VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE);
8993 }
8994
8995 if (cSectorsNeeded)
8996 {
8997 rc = vmdkAddFileBackedExtent(pImage, VMDK_SECTOR2BYTE(cSectorsNeeded));
8998 if (RT_FAILURE(rc))
8999 return rc;
9000 }
9001 }
9002 }
9003
9004 /**
9005 * monolithicSparse.
9006 */
9007 if (pExtent->enmType == VMDKETYPE_HOSTED_SPARSE && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G))
9008 {
9009 // 1. Calculate sectors needed for new overhead.
9010
9011 uint64_t cbNewOverhead = vmdkGetNewOverhead(pExtent, cSectorsNew);
9012 uint64_t cNewOverheadSectors = VMDK_BYTE2SECTOR(cbNewOverhead);
9013 uint64_t cOverheadSectorDiff = cNewOverheadSectors - pExtent->cOverheadSectors;
9014
9015 // 2. Relocate sectors to make room for new GD/GT, update entries in GD/GT
9016 if (cOverheadSectorDiff > 0)
9017 {
9018 if (pExtent->cSectors > 0)
9019 {
9020 /* Do the relocation. */
9021 LogFlow(("Relocating VMDK sectors\n"));
9022 rc = vmdkRelocateSectorsForSparseResize(pImage, pExtent, cSectorsNew);
9023 if (RT_FAILURE(rc))
9024 return rc;
9025
9026 rc = vmdkFlushImage(pImage, NULL);
9027 if (RT_FAILURE(rc))
9028 return rc;
9029 }
9030
9031 rc = vmdkResizeSparseMeta(pImage, pExtent, cSectorsNew);
9032 if (RT_FAILURE(rc))
9033 return rc;
9034 }
9035 }
9036
9037 /**
9038 * twoGbSparseExtent
9039 */
9040 if (pExtent->enmType == VMDKETYPE_HOSTED_SPARSE && (uImageFlags & VD_VMDK_IMAGE_FLAGS_SPLIT_2G))
9041 {
9042 /* Check to see how much space remains in last extent */
9043 bool fSpaceAvailible = false;
9044 uint64_t cLastExtentRemSectors = cSectorsOld % VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE);
9045 if (cLastExtentRemSectors)
9046 fSpaceAvailible = true;
9047
9048 uint64_t cSectorsNeeded = cSectorsNew - cSectorsOld;
9049
9050 if (fSpaceAvailible && cSectorsNeeded + cLastExtentRemSectors <= VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE))
9051 {
9052 pExtent = &pImage->pExtents[cExtents - 1];
9053 rc = vmdkRelocateSectorsForSparseResize(pImage, pExtent, cSectorsNeeded + cLastExtentRemSectors);
9054 if (RT_FAILURE(rc))
9055 return rc;
9056
9057 rc = vmdkFlushImage(pImage, NULL);
9058 if (RT_FAILURE(rc))
9059 return rc;
9060
9061 rc = vmdkResizeSparseMeta(pImage, pExtent, cSectorsNeeded + cLastExtentRemSectors);
9062 if (RT_FAILURE(rc))
9063 return rc;
9064 }
9065 else
9066 {
9067 if (fSpaceAvailible)
9068 {
9069 pExtent = &pImage->pExtents[cExtents - 1];
9070 rc = vmdkRelocateSectorsForSparseResize(pImage, pExtent, VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE));
9071 if (RT_FAILURE(rc))
9072 return rc;
9073
9074 rc = vmdkFlushImage(pImage, NULL);
9075 if (RT_FAILURE(rc))
9076 return rc;
9077
9078 rc = vmdkResizeSparseMeta(pImage, pExtent, VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE));
9079 if (RT_FAILURE(rc))
9080 return rc;
9081
9082 cSectorsNeeded = cSectorsNeeded - VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE) + cLastExtentRemSectors;
9083 }
9084
9085 unsigned cNewExtents = VMDK_SECTOR2BYTE(cSectorsNeeded) / VMDK_2G_SPLIT_SIZE;
9086 if (cNewExtents % VMDK_2G_SPLIT_SIZE || cNewExtents < VMDK_2G_SPLIT_SIZE)
9087 cNewExtents++;
9088
9089 for (unsigned i = cExtents;
9090 i < cExtents + cNewExtents && cSectorsNeeded >= VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE);
9091 i++)
9092 {
9093 rc = vmdkAddFileBackedExtent(pImage, VMDK_2G_SPLIT_SIZE);
9094 if (RT_FAILURE(rc))
9095 return rc;
9096
9097 pExtent = &pImage->pExtents[i];
9098
9099 rc = vmdkFlushImage(pImage, NULL);
9100 if (RT_FAILURE(rc))
9101 return rc;
9102
9103 pExtent->cSectors = VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE);
9104 cSectorsNeeded -= VMDK_BYTE2SECTOR(VMDK_2G_SPLIT_SIZE);
9105 }
9106
9107 if (cSectorsNeeded)
9108 {
9109 rc = vmdkAddFileBackedExtent(pImage, VMDK_SECTOR2BYTE(cSectorsNeeded));
9110 if (RT_FAILURE(rc))
9111 return rc;
9112
9113 pExtent = &pImage->pExtents[pImage->cExtents];
9114
9115 rc = vmdkFlushImage(pImage, NULL);
9116 if (RT_FAILURE(rc))
9117 return rc;
9118 }
9119 }
9120 }
9121
9122 /* Successful resize. Update metadata */
9123 if (RT_SUCCESS(rc))
9124 {
9125 /* Update size and new block count. */
9126 pImage->cbSize = cbSize;
9127 pExtent->cNominalSectors = cSectorsNew;
9128 pExtent->cSectors = cSectorsNew;
9129
9130 /* Update geometry. */
9131 pImage->PCHSGeometry = *pPCHSGeometry;
9132 pImage->LCHSGeometry = *pLCHSGeometry;
9133 }
9134
9135 /* Update header information in base image file. */
9136 pImage->Descriptor.fDirty = true;
9137 rc = vmdkWriteDescriptor(pImage, NULL);
9138
9139 if (RT_SUCCESS(rc))
9140 rc = vmdkFlushImage(pImage, NULL);
9141 }
9142 /* Same size doesn't change the image at all. */
9143
9144 LogFlowFunc(("returns %Rrc\n", rc));
9145 return rc;
9146}
9147
9148const VDIMAGEBACKEND g_VmdkBackend =
9149{
9150 /* u32Version */
9151 VD_IMGBACKEND_VERSION,
9152 /* pszBackendName */
9153 "VMDK",
9154 /* uBackendCaps */
9155 VD_CAP_UUID | VD_CAP_CREATE_FIXED | VD_CAP_CREATE_DYNAMIC
9156 | VD_CAP_CREATE_SPLIT_2G | VD_CAP_DIFF | VD_CAP_FILE | VD_CAP_ASYNC
9157 | VD_CAP_VFS | VD_CAP_PREFERRED,
9158 /* paFileExtensions */
9159 s_aVmdkFileExtensions,
9160 /* paConfigInfo */
9161 s_aVmdkConfigInfo,
9162 /* pfnProbe */
9163 vmdkProbe,
9164 /* pfnOpen */
9165 vmdkOpen,
9166 /* pfnCreate */
9167 vmdkCreate,
9168 /* pfnRename */
9169 vmdkRename,
9170 /* pfnClose */
9171 vmdkClose,
9172 /* pfnRead */
9173 vmdkRead,
9174 /* pfnWrite */
9175 vmdkWrite,
9176 /* pfnFlush */
9177 vmdkFlush,
9178 /* pfnDiscard */
9179 NULL,
9180 /* pfnGetVersion */
9181 vmdkGetVersion,
9182 /* pfnGetFileSize */
9183 vmdkGetFileSize,
9184 /* pfnGetPCHSGeometry */
9185 vmdkGetPCHSGeometry,
9186 /* pfnSetPCHSGeometry */
9187 vmdkSetPCHSGeometry,
9188 /* pfnGetLCHSGeometry */
9189 vmdkGetLCHSGeometry,
9190 /* pfnSetLCHSGeometry */
9191 vmdkSetLCHSGeometry,
9192 /* pfnQueryRegions */
9193 vmdkQueryRegions,
9194 /* pfnRegionListRelease */
9195 vmdkRegionListRelease,
9196 /* pfnGetImageFlags */
9197 vmdkGetImageFlags,
9198 /* pfnGetOpenFlags */
9199 vmdkGetOpenFlags,
9200 /* pfnSetOpenFlags */
9201 vmdkSetOpenFlags,
9202 /* pfnGetComment */
9203 vmdkGetComment,
9204 /* pfnSetComment */
9205 vmdkSetComment,
9206 /* pfnGetUuid */
9207 vmdkGetUuid,
9208 /* pfnSetUuid */
9209 vmdkSetUuid,
9210 /* pfnGetModificationUuid */
9211 vmdkGetModificationUuid,
9212 /* pfnSetModificationUuid */
9213 vmdkSetModificationUuid,
9214 /* pfnGetParentUuid */
9215 vmdkGetParentUuid,
9216 /* pfnSetParentUuid */
9217 vmdkSetParentUuid,
9218 /* pfnGetParentModificationUuid */
9219 vmdkGetParentModificationUuid,
9220 /* pfnSetParentModificationUuid */
9221 vmdkSetParentModificationUuid,
9222 /* pfnDump */
9223 vmdkDump,
9224 /* pfnGetTimestamp */
9225 NULL,
9226 /* pfnGetParentTimestamp */
9227 NULL,
9228 /* pfnSetParentTimestamp */
9229 NULL,
9230 /* pfnGetParentFilename */
9231 NULL,
9232 /* pfnSetParentFilename */
9233 NULL,
9234 /* pfnComposeLocation */
9235 genericFileComposeLocation,
9236 /* pfnComposeName */
9237 genericFileComposeName,
9238 /* pfnCompact */
9239 NULL,
9240 /* pfnResize */
9241 vmdkResize,
9242 /* pfnRepair */
9243 NULL,
9244 /* pfnTraverseMetadata */
9245 NULL,
9246 /* u32VersionEnd */
9247 VD_IMGBACKEND_VERSION
9248};
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use