From c37a305de67059fa47434e298c5d4a67d932d858 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Thu, 1 Oct 2009 03:09:18 +0000 Subject: [PATCH] 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 --- .../kernel/partitioning_systems/Jamfile | 13 +- .../kernel/partitioning_systems/vmdk/Jamfile | 10 + .../kernel/partitioning_systems/vmdk/vmdk.cpp | 517 ++++++++++++++++++ 3 files changed, 534 insertions(+), 6 deletions(-) create mode 100644 src/add-ons/kernel/partitioning_systems/vmdk/Jamfile create mode 100644 src/add-ons/kernel/partitioning_systems/vmdk/vmdk.cpp diff --git a/src/add-ons/kernel/partitioning_systems/Jamfile b/src/add-ons/kernel/partitioning_systems/Jamfile index 58a0e13025..353620fac0 100644 --- a/src/add-ons/kernel/partitioning_systems/Jamfile +++ b/src/add-ons/kernel/partitioning_systems/Jamfile @@ -1,8 +1,9 @@ SubDir HAIKU_TOP src add-ons kernel partitioning_systems ; -SubInclude HAIKU_TOP src add-ons kernel partitioning_systems amiga ; -SubInclude HAIKU_TOP src add-ons kernel partitioning_systems apple ; -SubInclude HAIKU_TOP src add-ons kernel partitioning_systems atari ; -SubInclude HAIKU_TOP src add-ons kernel partitioning_systems efi ; -SubInclude HAIKU_TOP src add-ons kernel partitioning_systems intel ; -SubInclude HAIKU_TOP src add-ons kernel partitioning_systems session ; +HaikuSubInclude amiga ; +HaikuSubInclude apple ; +HaikuSubInclude atari ; +HaikuSubInclude efi ; +HaikuSubInclude intel ; +HaikuSubInclude session ; +HaikuSubInclude vmdk ; diff --git a/src/add-ons/kernel/partitioning_systems/vmdk/Jamfile b/src/add-ons/kernel/partitioning_systems/vmdk/Jamfile new file mode 100644 index 0000000000..25ebbd8ff5 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/vmdk/Jamfile @@ -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 ; diff --git a/src/add-ons/kernel/partitioning_systems/vmdk/vmdk.cpp b/src/add-ons/kernel/partitioning_systems/vmdk/vmdk.cpp new file mode 100644 index 0000000000..207a87e6e0 --- /dev/null +++ b/src/add-ons/kernel/partitioning_systems/vmdk/vmdk.cpp @@ -0,0 +1,517 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include + +//#define TRACE_VMDK 1 +#ifdef _BOOT_MODE +# include +# include +# undef TRACE_VMDK +#else +# include +#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 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