2 * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
13 #include <KernelExport.h>
15 #include <AutoDeleter.h>
16 #include <ddm_modules.h>
17 #include <disk_device_types.h>
21 //#define TRACE_VMDK 1
23 # include <boot/partitions.h>
24 # include <util/kernel_cpp.h>
27 # include <DiskDeviceTypes.h>
31 # define TRACE(x...) dprintf("vmdk: " x)
33 # define TRACE(x...) do { } while (false)
38 #define VMDK_PARTITION_MODULE_NAME "partitioning_systems/vmdk/v1"
41 // #pragma mark - VMDK header/descriptor parsing
44 static const off_t kMaxDescriptorSize
= 64 * 1024;
48 VmdkCookie(off_t contentOffset
, off_t contentSize
)
50 contentOffset(contentOffset
),
51 contentSize(contentSize
)
95 if (length
+ 1 < sizeof(string
)) {
97 string
[length
] = '\0';
101 bool operator==(const char* other
) const
103 return strcmp(string
, other
) == 0;
106 bool operator!=(const char* other
) const
108 return !(*this == other
);
114 read_file(int fd
, off_t offset
, void* buffer
, size_t size
)
116 ssize_t bytesRead
= pread(fd
, buffer
, size
, offset
);
120 return (size_t)bytesRead
== size
? B_OK
: B_ERROR
;
125 next_token(char*& line
, const char* lineEnd
, Token
& token
)
128 while (line
!= lineEnd
&& isspace(*line
))
131 // comment/end of line
132 if (line
== lineEnd
|| *line
== '#') {
150 while (line
!= lineEnd
) {
164 token
.PushChar(*(line
++));
174 while (line
!= lineEnd
&& *line
!= '#' && *line
!= '='
175 && !isspace(*line
)) {
176 token
.PushChar(*(line
++));
185 parse_vmdk_header(int fd
, off_t fileSize
, VmdkCookie
*& _cookie
)
188 SparseExtentHeader header
;
189 status_t error
= read_file(fd
, 0, &header
, sizeof(header
));
194 if (header
.magicNumber
!= VMDK_SPARSE_MAGICNUMBER
) {
195 TRACE("Error: Header magic mismatch!\n");
199 if (header
.version
!= VMDK_SPARSE_VERSION
) {
200 TRACE("Error: Header version mismatch!\n");
204 if (header
.overHead
> (uint64_t)fileSize
/ 512) {
205 TRACE("Error: Header overHead invalid!\n");
208 off_t headerSize
= header
.overHead
* 512;
210 if (header
.descriptorOffset
< (sizeof(header
) + 511) / 512
211 || header
.descriptorOffset
>= header
.overHead
212 || header
.descriptorSize
== 0
213 || header
.overHead
- header
.descriptorOffset
< header
.descriptorSize
) {
214 TRACE("Error: Invalid descriptor location!\n");
217 off_t descriptorOffset
= header
.descriptorOffset
* 512;
218 off_t descriptorSize
= header
.descriptorSize
* 512;
220 if (descriptorSize
> kMaxDescriptorSize
) {
221 TRACE("Error: Unsupported descriptor size!\n");
222 return B_UNSUPPORTED
;
226 char* descriptor
= (char*)malloc(descriptorSize
+ 1);
227 if (descriptor
== NULL
) {
228 TRACE("Error: Descriptor allocation failed!\n");
231 MemoryDeleter
descriptorDeleter(descriptor
);
233 error
= read_file(fd
, descriptorOffset
, descriptor
, descriptorSize
);
237 // determine the actual descriptor size
238 descriptor
[descriptorSize
] = '\0';
239 descriptorSize
= strlen(descriptor
);
242 uint64_t extendOffset
= 0;
243 uint64_t extendSize
= 0;
245 char* line
= descriptor
;
246 char* descriptorEnd
= line
+ descriptorSize
;
247 while (line
< descriptorEnd
) {
248 // determine the end of the line
249 char* lineEnd
= strchr(line
, '\n');
253 lineEnd
= descriptorEnd
;
256 if (next_token(line
, lineEnd
, token
) == TOKEN_END
) {
262 switch (next_token(line
, lineEnd
, token2
)) {
267 if (next_token(line
, lineEnd
, token2
) != TOKEN_STRING
) {
268 TRACE("Line not understood: %s = ?\n", token
.string
);
272 if (token
== "version") {
274 TRACE("Unsupported descriptor version: %s\n",
276 return B_UNSUPPORTED
;
278 } else if (token
== "createType") {
279 if (token2
!= "monolithicFlat") {
280 TRACE("Unsupported descriptor createType: %s\n",
282 return B_UNSUPPORTED
;
292 extendSize
= strtoll(token2
.string
, NULL
, 0);
293 if (extendSize
== 0) {
294 TRACE("Bad extend size.\n");
298 if (next_token(line
, lineEnd
, token
) != TOKEN_STRING
300 || next_token(line
, lineEnd
, token
) != TOKEN_STRING
302 || next_token(line
, lineEnd
, token2
) != TOKEN_STRING
) {
303 TRACE("Invalid/unsupported extend line\n");
307 extendOffset
= strtoll(token2
.string
, NULL
, 0);
308 if (extendOffset
== 0) {
309 TRACE("Bad extend offset.\n");
319 if (extendOffset
< (uint64_t)headerSize
/ 512
320 || extendOffset
>= (uint64_t)fileSize
/ 512
322 || (uint64_t)fileSize
/ 512 - extendOffset
< extendSize
) {
323 TRACE("Error: Invalid extend location!\n");
327 TRACE("descriptor len: %lld\n", descriptorSize
);
328 TRACE("header size: %lld\n", headerSize
);
329 TRACE("file size: %lld\n", fileSize
);
330 TRACE("extend offset: %lld\n", extendOffset
* 512);
331 TRACE("extend size: %lld\n", extendSize
* 512);
333 VmdkCookie
* cookie
= new(std::nothrow
) VmdkCookie(extendOffset
* 512,
343 // #pragma mark - module hooks
347 vmdk_std_ops(int32 op
, ...)
349 TRACE("vmdk_std_ops(0x%lx)\n", op
);
352 case B_MODULE_UNINIT
:
360 vmdk_identify_partition(int fd
, partition_data
* partition
, void** _cookie
)
362 TRACE("vmdk_identify_partition(%d, %ld: %lld, %lld, %ld)\n", fd
,
363 partition
->id
, partition
->offset
, partition
->size
,
364 partition
->block_size
);
367 status_t error
= parse_vmdk_header(fd
, partition
->size
, cookie
);
377 vmdk_scan_partition(int fd
, partition_data
* partition
, void* _cookie
)
379 TRACE("vmdk_scan_partition(%d, %ld: %lld, %lld, %ld)\n", fd
,
380 partition
->id
, partition
->offset
, partition
->size
,
381 partition
->block_size
);
383 VmdkCookie
* cookie
= (VmdkCookie
*)_cookie
;
384 ObjectDeleter
<VmdkCookie
> cookieDeleter(cookie
);
386 // fill in the partition_data structure
387 partition
->status
= B_PARTITION_VALID
;
388 partition
->flags
|= B_PARTITION_PARTITIONING_SYSTEM
;
389 partition
->content_size
= partition
->size
;
390 // (no content_name and content_parameters)
391 // (content_type is set by the system)
392 partition
->content_cookie
= cookie
;
395 partition_data
* child
= create_child_partition(partition
->id
, 0,
396 partition
->offset
+ cookie
->contentOffset
, cookie
->contentSize
, -1);
398 partition
->content_cookie
= NULL
;
402 child
->block_size
= partition
->block_size
;
404 child
->type
= strdup(kPartitionTypeUnrecognized
);
405 child
->parameters
= NULL
;
406 child
->cookie
= NULL
;
408 // check for allocation problems
409 if (child
->type
== NULL
) {
410 partition
->content_cookie
= NULL
;
414 cookieDeleter
.Detach();
420 vmdk_free_identify_partition_cookie(partition_data*/
* partition*/
, void* cookie
)
422 delete (VmdkCookie
*)cookie
;
427 vmdk_free_partition_cookie(partition_data
* partition
)
429 // called for the child partition -- it doesn't have a cookie
434 vmdk_free_partition_content_cookie(partition_data
* partition
)
436 delete (VmdkCookie
*)partition
->content_cookie
;
441 partition_module_info gVMwarePartitionModule
=
443 static partition_module_info vmdk_partition_module
=
447 VMDK_PARTITION_MODULE_NAME
,
451 "vmdk", // short_name
452 VMDK_PARTITION_NAME
, // pretty_name
458 vmdk_identify_partition
, // identify_partition
459 vmdk_scan_partition
, // scan_partition
460 vmdk_free_identify_partition_cookie
, // free_identify_partition_cookie
461 vmdk_free_partition_cookie
, // free_partition_cookie
462 vmdk_free_partition_content_cookie
, // free_partition_content_cookie
471 extern "C" partition_module_info
* modules
[];
472 _EXPORT partition_module_info
* modules
[] =
474 &vmdk_partition_module
,