Implemented a "vmdk" partitioning system. It allows to access the contents of
monolithic vmware images as produced by the build system (by registering such an image via the "diskimage" tool). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@33388 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
SubDir HAIKU_TOP src add-ons kernel partitioning_systems ;
|
SubDir HAIKU_TOP src add-ons kernel partitioning_systems ;
|
||||||
|
|
||||||
SubInclude HAIKU_TOP src add-ons kernel partitioning_systems amiga ;
|
HaikuSubInclude amiga ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel partitioning_systems apple ;
|
HaikuSubInclude apple ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel partitioning_systems atari ;
|
HaikuSubInclude atari ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel partitioning_systems efi ;
|
HaikuSubInclude efi ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel partitioning_systems intel ;
|
HaikuSubInclude intel ;
|
||||||
SubInclude HAIKU_TOP src add-ons kernel partitioning_systems session ;
|
HaikuSubInclude session ;
|
||||||
|
HaikuSubInclude vmdk ;
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
SubDir HAIKU_TOP src add-ons kernel partitioning_systems vmdk ;
|
||||||
|
|
||||||
|
UsePrivateHeaders [ FDirName kernel disk_device_manager ] ;
|
||||||
|
UsePrivateHeaders kernel ;
|
||||||
|
UsePrivateHeaders shared ;
|
||||||
|
UsePrivateHeaders storage ;
|
||||||
|
UsePrivateHeaders vmdk ;
|
||||||
|
UsePrivateSystemHeaders ;
|
||||||
|
|
||||||
|
KernelAddon vmdk : vmdk.cpp ;
|
||||||
@@ -0,0 +1,517 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <AutoDeleter.h>
|
||||||
|
#include <ddm_modules.h>
|
||||||
|
#include <disk_device_types.h>
|
||||||
|
|
||||||
|
#include <vmdk.h>
|
||||||
|
|
||||||
|
//#define TRACE_VMDK 1
|
||||||
|
#ifdef _BOOT_MODE
|
||||||
|
# include <boot/partitions.h>
|
||||||
|
# include <util/kernel_cpp.h>
|
||||||
|
# undef TRACE_VMDK
|
||||||
|
#else
|
||||||
|
# include <DiskDeviceTypes.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if TRACE_VMDK
|
||||||
|
# define TRACE(x...) dprintf("vmdk: " x)
|
||||||
|
#else
|
||||||
|
# define TRACE(x...) do { } while (false)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// module name
|
||||||
|
#define VMDK_PARTITION_MODULE_NAME "partitioning_systems/vmdk/v1"
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - VMDK header/descriptor parsing
|
||||||
|
|
||||||
|
|
||||||
|
static const off_t kMaxDescriptorSize = 64 * 1024;
|
||||||
|
|
||||||
|
|
||||||
|
struct VmdkCookie {
|
||||||
|
VmdkCookie(off_t contentOffset, off_t contentSize)
|
||||||
|
:
|
||||||
|
contentOffset(contentOffset),
|
||||||
|
contentSize(contentSize)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t contentOffset;
|
||||||
|
off_t contentSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TOKEN_END,
|
||||||
|
TOKEN_STRING,
|
||||||
|
TOKEN_ASSIGN
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Token {
|
||||||
|
int type;
|
||||||
|
size_t length;
|
||||||
|
char string[1024];
|
||||||
|
|
||||||
|
void SetToEnd()
|
||||||
|
{
|
||||||
|
type = TOKEN_END;
|
||||||
|
string[0] = '\0';
|
||||||
|
length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetToAssign()
|
||||||
|
{
|
||||||
|
type = TOKEN_ASSIGN;
|
||||||
|
string[0] = '=';
|
||||||
|
string[1] = '\0';
|
||||||
|
length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetToString()
|
||||||
|
{
|
||||||
|
type = TOKEN_STRING;
|
||||||
|
string[0] = '\0';
|
||||||
|
length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushChar(char c)
|
||||||
|
{
|
||||||
|
if (length + 1 < sizeof(string)) {
|
||||||
|
string[length++] = c;
|
||||||
|
string[length] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const char* other) const
|
||||||
|
{
|
||||||
|
return strcmp(string, other) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const char* other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
read_file(int fd, off_t offset, void* buffer, size_t size)
|
||||||
|
{
|
||||||
|
ssize_t bytesRead = pread(fd, buffer, size, offset);
|
||||||
|
if (bytesRead < 0)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
return (size_t)bytesRead == size ? B_OK : B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
next_token(char*& line, const char* lineEnd, Token& token)
|
||||||
|
{
|
||||||
|
// skip whitespace
|
||||||
|
while (line != lineEnd && isspace(*line))
|
||||||
|
line++;
|
||||||
|
|
||||||
|
// comment/end of line
|
||||||
|
if (line == lineEnd || *line == '#') {
|
||||||
|
token.SetToEnd();
|
||||||
|
return token.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (*line) {
|
||||||
|
case '=':
|
||||||
|
{
|
||||||
|
line++;
|
||||||
|
token.SetToAssign();
|
||||||
|
return token.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '"':
|
||||||
|
{
|
||||||
|
// quoted string
|
||||||
|
token.SetToString();
|
||||||
|
line++;
|
||||||
|
while (line != lineEnd) {
|
||||||
|
if (*line == '"') {
|
||||||
|
// end of string
|
||||||
|
line++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*line == '\\') {
|
||||||
|
// escaped char
|
||||||
|
line++;
|
||||||
|
if (line == lineEnd)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
token.PushChar(*(line++));
|
||||||
|
}
|
||||||
|
|
||||||
|
return token.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// unquoted string
|
||||||
|
token.SetToString();
|
||||||
|
while (line != lineEnd && *line != '#' && *line != '='
|
||||||
|
&& !isspace(*line)) {
|
||||||
|
token.PushChar(*(line++));
|
||||||
|
}
|
||||||
|
return token.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
parse_vmdk_header(int fd, off_t fileSize, VmdkCookie*& _cookie)
|
||||||
|
{
|
||||||
|
// read the header
|
||||||
|
SparseExtentHeader header;
|
||||||
|
status_t error = read_file(fd, 0, &header, sizeof(header));
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
// check the header
|
||||||
|
if (header.magicNumber != VMDK_SPARSE_MAGICNUMBER) {
|
||||||
|
TRACE("Error: Header magic mismatch!\n");
|
||||||
|
return B_BAD_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.version != VMDK_SPARSE_VERSION) {
|
||||||
|
TRACE("Error: Header version mismatch!\n");
|
||||||
|
return B_BAD_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.overHead > (uint64_t)fileSize / 512) {
|
||||||
|
TRACE("Error: Header overHead invalid!\n");
|
||||||
|
return B_BAD_DATA;
|
||||||
|
}
|
||||||
|
off_t headerSize = header.overHead * 512;
|
||||||
|
|
||||||
|
if (header.descriptorOffset < (sizeof(header) + 511) / 512
|
||||||
|
|| header.descriptorOffset >= header.overHead
|
||||||
|
|| header.descriptorSize == 0
|
||||||
|
|| header.overHead - header.descriptorOffset < header.descriptorSize) {
|
||||||
|
TRACE("Error: Invalid descriptor location!\n");
|
||||||
|
return B_BAD_DATA;
|
||||||
|
}
|
||||||
|
off_t descriptorOffset = header.descriptorOffset * 512;
|
||||||
|
off_t descriptorSize = header.descriptorSize * 512;
|
||||||
|
|
||||||
|
if (descriptorSize > kMaxDescriptorSize) {
|
||||||
|
TRACE("Error: Unsupported descriptor size!\n");
|
||||||
|
return B_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read descriptor
|
||||||
|
char* descriptor = (char*)malloc(descriptorSize + 1);
|
||||||
|
if (descriptor == NULL) {
|
||||||
|
TRACE("Error: Descriptor allocation failed!\n");
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
MemoryDeleter descriptorDeleter(descriptor);
|
||||||
|
|
||||||
|
error = read_file(fd, descriptorOffset, descriptor, descriptorSize);
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
// determine the actual descriptor size
|
||||||
|
descriptor[descriptorSize] = '\0';
|
||||||
|
descriptorSize = strlen(descriptor);
|
||||||
|
|
||||||
|
// parse descriptor
|
||||||
|
uint64_t extendOffset = 0;
|
||||||
|
uint64_t extendSize = 0;
|
||||||
|
|
||||||
|
char* line = descriptor;
|
||||||
|
char* descriptorEnd = line + descriptorSize;
|
||||||
|
while (line < descriptorEnd) {
|
||||||
|
// determine the end of the line
|
||||||
|
char* lineEnd = strchr(line, '\n');
|
||||||
|
if (lineEnd != NULL)
|
||||||
|
*lineEnd = '\0';
|
||||||
|
else
|
||||||
|
lineEnd = descriptorEnd;
|
||||||
|
|
||||||
|
Token token;
|
||||||
|
if (next_token(line, lineEnd, token) == TOKEN_END) {
|
||||||
|
line = lineEnd + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token token2;
|
||||||
|
switch (next_token(line, lineEnd, token2)) {
|
||||||
|
case TOKEN_END:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOKEN_ASSIGN:
|
||||||
|
if (next_token(line, lineEnd, token2) != TOKEN_STRING) {
|
||||||
|
TRACE("Line not understood: %s = ?\n", token.string);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token == "version") {
|
||||||
|
if (token2 != "1") {
|
||||||
|
TRACE("Unsupported descriptor version: %s\n",
|
||||||
|
token2.string);
|
||||||
|
return B_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
} else if (token == "createType") {
|
||||||
|
if (token2 != "monolithicFlat") {
|
||||||
|
TRACE("Unsupported descriptor createType: %s\n",
|
||||||
|
token2.string);
|
||||||
|
return B_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOKEN_STRING:
|
||||||
|
if (token != "RW")
|
||||||
|
break;
|
||||||
|
|
||||||
|
extendSize = strtoll(token2.string, NULL, 0);
|
||||||
|
if (extendSize == 0) {
|
||||||
|
TRACE("Bad extend size.\n");
|
||||||
|
return B_BAD_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next_token(line, lineEnd, token) != TOKEN_STRING
|
||||||
|
|| token != "FLAT"
|
||||||
|
|| next_token(line, lineEnd, token) != TOKEN_STRING
|
||||||
|
// image name
|
||||||
|
|| next_token(line, lineEnd, token2) != TOKEN_STRING) {
|
||||||
|
TRACE("Invalid/unsupported extend line\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
extendOffset = strtoll(token2.string, 0, NULL);
|
||||||
|
if (extendOffset == 0) {
|
||||||
|
TRACE("Bad extend offset.\n");
|
||||||
|
return B_BAD_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
line = lineEnd + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extendOffset < (uint64_t)headerSize / 512
|
||||||
|
|| extendOffset >= (uint64_t)fileSize / 512
|
||||||
|
|| extendSize == 0
|
||||||
|
|| (uint64_t)fileSize / 512 - extendOffset < extendSize) {
|
||||||
|
TRACE("Error: Invalid extend location!\n");
|
||||||
|
return B_BAD_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE("descriptor len: %lld\n", descriptorSize);
|
||||||
|
TRACE("header size: %lld\n", headerSize);
|
||||||
|
TRACE("file size: %lld\n", fileSize);
|
||||||
|
TRACE("extend offset: %lld\n", extendOffset * 512);
|
||||||
|
TRACE("extend size: %lld\n", extendSize * 512);
|
||||||
|
|
||||||
|
VmdkCookie* cookie = new(std::nothrow) VmdkCookie(extendOffset * 512,
|
||||||
|
extendSize * 512);
|
||||||
|
if (cookie == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
_cookie = cookie;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - module hooks
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
vmdk_std_ops(int32 op, ...)
|
||||||
|
{
|
||||||
|
TRACE("vmdk_std_ops(0x%lx)\n", op);
|
||||||
|
switch(op) {
|
||||||
|
case B_MODULE_INIT:
|
||||||
|
case B_MODULE_UNINIT:
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static float
|
||||||
|
vmdk_identify_partition(int fd, partition_data* partition, void** _cookie)
|
||||||
|
{
|
||||||
|
TRACE("vmdk_identify_partition(%d, %ld: %lld, %lld, %ld)\n", fd,
|
||||||
|
partition->id, partition->offset, partition->size,
|
||||||
|
partition->block_size);
|
||||||
|
|
||||||
|
VmdkCookie* cookie;
|
||||||
|
status_t error = parse_vmdk_header(fd, partition->size, cookie);
|
||||||
|
if (error != B_OK)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
*_cookie = cookie;
|
||||||
|
return 0.8f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
vmdk_scan_partition(int fd, partition_data* partition, void* _cookie)
|
||||||
|
{
|
||||||
|
TRACE("vmdk_scan_partition(%d, %ld: %lld, %lld, %ld)\n", fd,
|
||||||
|
partition->id, partition->offset, partition->size,
|
||||||
|
partition->block_size);
|
||||||
|
|
||||||
|
VmdkCookie* cookie = (VmdkCookie*)_cookie;
|
||||||
|
ObjectDeleter<VmdkCookie> cookieDeleter(cookie);
|
||||||
|
|
||||||
|
// fill in the partition_data structure
|
||||||
|
partition->status = B_PARTITION_VALID;
|
||||||
|
partition->flags |= B_PARTITION_PARTITIONING_SYSTEM;
|
||||||
|
partition->content_size = partition->size;
|
||||||
|
// (no content_name and content_parameters)
|
||||||
|
// (content_type is set by the system)
|
||||||
|
partition->content_cookie = cookie;
|
||||||
|
|
||||||
|
// child
|
||||||
|
partition_data* child = create_child_partition(partition->id, 0,
|
||||||
|
partition->offset + cookie->contentOffset, cookie->contentSize, -1);
|
||||||
|
if (child == NULL) {
|
||||||
|
partition->content_cookie = NULL;
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
child->block_size = partition->block_size;
|
||||||
|
// (no name)
|
||||||
|
child->type = strdup(kPartitionTypeUnrecognized);
|
||||||
|
child->parameters = NULL;
|
||||||
|
child->cookie = NULL;
|
||||||
|
|
||||||
|
// check for allocation problems
|
||||||
|
if (child->type == NULL) {
|
||||||
|
partition->content_cookie = NULL;
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
cookieDeleter.Detach();
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
vmdk_free_identify_partition_cookie(partition_data*/* partition*/, void* cookie)
|
||||||
|
{
|
||||||
|
delete (VmdkCookie*)cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
vmdk_free_partition_cookie(partition_data* partition)
|
||||||
|
{
|
||||||
|
// called for the child partition -- it doesn't have a cookie
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
vmdk_free_partition_content_cookie(partition_data* partition)
|
||||||
|
{
|
||||||
|
delete (VmdkCookie*)partition->content_cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _BOOT_MODE
|
||||||
|
partition_module_info gVMwarePartitionModule =
|
||||||
|
#else
|
||||||
|
static partition_module_info vmdk_partition_module =
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
{
|
||||||
|
VMDK_PARTITION_MODULE_NAME,
|
||||||
|
0,
|
||||||
|
vmdk_std_ops
|
||||||
|
},
|
||||||
|
"vmdk", // short_name
|
||||||
|
VMDK_PARTITION_NAME, // pretty_name
|
||||||
|
|
||||||
|
// flags
|
||||||
|
0,
|
||||||
|
|
||||||
|
// scanning
|
||||||
|
vmdk_identify_partition, // identify_partition
|
||||||
|
vmdk_scan_partition, // scan_partition
|
||||||
|
vmdk_free_identify_partition_cookie, // free_identify_partition_cookie
|
||||||
|
vmdk_free_partition_cookie, // free_partition_cookie
|
||||||
|
vmdk_free_partition_content_cookie, // free_partition_content_cookie
|
||||||
|
|
||||||
|
#ifndef _BOOT_MODE
|
||||||
|
// querying (obsolete)
|
||||||
|
NULL, // get_supported_operations
|
||||||
|
NULL, // get_supported_child_operations
|
||||||
|
NULL, // supports_initializing_child
|
||||||
|
NULL, // is_sub_system_for
|
||||||
|
|
||||||
|
// validation hooks (obsolete)
|
||||||
|
NULL, // validate_resize
|
||||||
|
NULL, // validate_resize_child
|
||||||
|
NULL, // validate_move
|
||||||
|
NULL, // validate_move_child
|
||||||
|
NULL, // validate_set_name
|
||||||
|
NULL, // validate_set_content_name
|
||||||
|
NULL, // validate_set_type
|
||||||
|
NULL, // validate_set_parameters
|
||||||
|
NULL, // validate_set_content_parameters
|
||||||
|
NULL, // validate_initialize
|
||||||
|
NULL, // validate_create_child
|
||||||
|
NULL, // get_partitionable_spaces
|
||||||
|
NULL, // get_next_supported_type
|
||||||
|
NULL, // get_type_for_content_type
|
||||||
|
|
||||||
|
// shadow partition modification (obsolete)
|
||||||
|
NULL, // shadow_changed
|
||||||
|
|
||||||
|
// writing
|
||||||
|
NULL, // repair
|
||||||
|
NULL, // resize
|
||||||
|
NULL, // resize_child
|
||||||
|
NULL, // move
|
||||||
|
NULL, // move_child
|
||||||
|
NULL, // set_name
|
||||||
|
NULL, // set_content_name
|
||||||
|
NULL, // set_type
|
||||||
|
NULL, // set_parameters
|
||||||
|
NULL, // set_content_parameters
|
||||||
|
NULL, // initialize
|
||||||
|
NULL, // create_child
|
||||||
|
NULL, // delete_child
|
||||||
|
#else
|
||||||
|
NULL
|
||||||
|
#endif // _BOOT_MODE
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _BOOT_MODE
|
||||||
|
extern "C" partition_module_info* modules[];
|
||||||
|
_EXPORT partition_module_info* modules[] =
|
||||||
|
{
|
||||||
|
&vmdk_partition_module,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user