BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / partitioning_systems / vmdk / vmdk.cpp
blob7e6962a338a22720c2eb267ed412c23837976d8c
1 /*
2 * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
7 #include <ctype.h>
8 #include <errno.h>
9 #include <string.h>
11 #include <new>
13 #include <KernelExport.h>
15 #include <AutoDeleter.h>
16 #include <ddm_modules.h>
17 #include <disk_device_types.h>
19 #include <vmdk.h>
21 //#define TRACE_VMDK 1
22 #ifdef _BOOT_MODE
23 # include <boot/partitions.h>
24 # include <util/kernel_cpp.h>
25 # undef TRACE_VMDK
26 #else
27 # include <DiskDeviceTypes.h>
28 #endif
30 #if TRACE_VMDK
31 # define TRACE(x...) dprintf("vmdk: " x)
32 #else
33 # define TRACE(x...) do { } while (false)
34 #endif
37 // module name
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;
47 struct VmdkCookie {
48 VmdkCookie(off_t contentOffset, off_t contentSize)
50 contentOffset(contentOffset),
51 contentSize(contentSize)
55 off_t contentOffset;
56 off_t contentSize;
60 enum {
61 TOKEN_END,
62 TOKEN_STRING,
63 TOKEN_ASSIGN
66 struct Token {
67 int type;
68 size_t length;
69 char string[1024];
71 void SetToEnd()
73 type = TOKEN_END;
74 string[0] = '\0';
75 length = 0;
78 void SetToAssign()
80 type = TOKEN_ASSIGN;
81 string[0] = '=';
82 string[1] = '\0';
83 length = 0;
86 void SetToString()
88 type = TOKEN_STRING;
89 string[0] = '\0';
90 length = 0;
93 void PushChar(char c)
95 if (length + 1 < sizeof(string)) {
96 string[length++] = c;
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);
113 static status_t
114 read_file(int fd, off_t offset, void* buffer, size_t size)
116 ssize_t bytesRead = pread(fd, buffer, size, offset);
117 if (bytesRead < 0)
118 return errno;
120 return (size_t)bytesRead == size ? B_OK : B_ERROR;
124 static int
125 next_token(char*& line, const char* lineEnd, Token& token)
127 // skip whitespace
128 while (line != lineEnd && isspace(*line))
129 line++;
131 // comment/end of line
132 if (line == lineEnd || *line == '#') {
133 token.SetToEnd();
134 return token.type;
137 switch (*line) {
138 case '=':
140 line++;
141 token.SetToAssign();
142 return token.type;
145 case '"':
147 // quoted string
148 token.SetToString();
149 line++;
150 while (line != lineEnd) {
151 if (*line == '"') {
152 // end of string
153 line++;
154 break;
157 if (*line == '\\') {
158 // escaped char
159 line++;
160 if (line == lineEnd)
161 break;
164 token.PushChar(*(line++));
167 return token.type;
170 default:
172 // unquoted string
173 token.SetToString();
174 while (line != lineEnd && *line != '#' && *line != '='
175 && !isspace(*line)) {
176 token.PushChar(*(line++));
178 return token.type;
184 static status_t
185 parse_vmdk_header(int fd, off_t fileSize, VmdkCookie*& _cookie)
187 // read the header
188 SparseExtentHeader header;
189 status_t error = read_file(fd, 0, &header, sizeof(header));
190 if (error != B_OK)
191 return error;
193 // check the header
194 if (header.magicNumber != VMDK_SPARSE_MAGICNUMBER) {
195 TRACE("Error: Header magic mismatch!\n");
196 return B_BAD_DATA;
199 if (header.version != VMDK_SPARSE_VERSION) {
200 TRACE("Error: Header version mismatch!\n");
201 return B_BAD_DATA;
204 if (header.overHead > (uint64_t)fileSize / 512) {
205 TRACE("Error: Header overHead invalid!\n");
206 return B_BAD_DATA;
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");
215 return B_BAD_DATA;
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;
225 // read descriptor
226 char* descriptor = (char*)malloc(descriptorSize + 1);
227 if (descriptor == NULL) {
228 TRACE("Error: Descriptor allocation failed!\n");
229 return B_NO_MEMORY;
231 MemoryDeleter descriptorDeleter(descriptor);
233 error = read_file(fd, descriptorOffset, descriptor, descriptorSize);
234 if (error != B_OK)
235 return error;
237 // determine the actual descriptor size
238 descriptor[descriptorSize] = '\0';
239 descriptorSize = strlen(descriptor);
241 // parse 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');
250 if (lineEnd != NULL)
251 *lineEnd = '\0';
252 else
253 lineEnd = descriptorEnd;
255 Token token;
256 if (next_token(line, lineEnd, token) == TOKEN_END) {
257 line = lineEnd + 1;
258 continue;
261 Token token2;
262 switch (next_token(line, lineEnd, token2)) {
263 case TOKEN_END:
264 break;
266 case TOKEN_ASSIGN:
267 if (next_token(line, lineEnd, token2) != TOKEN_STRING) {
268 TRACE("Line not understood: %s = ?\n", token.string);
269 break;
272 if (token == "version") {
273 if (token2 != "1") {
274 TRACE("Unsupported descriptor version: %s\n",
275 token2.string);
276 return B_UNSUPPORTED;
278 } else if (token == "createType") {
279 if (token2 != "monolithicFlat") {
280 TRACE("Unsupported descriptor createType: %s\n",
281 token2.string);
282 return B_UNSUPPORTED;
286 break;
288 case TOKEN_STRING:
289 if (token != "RW")
290 break;
292 extendSize = strtoll(token2.string, NULL, 0);
293 if (extendSize == 0) {
294 TRACE("Bad extend size.\n");
295 return B_BAD_DATA;
298 if (next_token(line, lineEnd, token) != TOKEN_STRING
299 || token != "FLAT"
300 || next_token(line, lineEnd, token) != TOKEN_STRING
301 // image name
302 || next_token(line, lineEnd, token2) != TOKEN_STRING) {
303 TRACE("Invalid/unsupported extend line\n");
304 break;
307 extendOffset = strtoll(token2.string, NULL, 0);
308 if (extendOffset == 0) {
309 TRACE("Bad extend offset.\n");
310 return B_BAD_DATA;
313 break;
316 line = lineEnd + 1;
319 if (extendOffset < (uint64_t)headerSize / 512
320 || extendOffset >= (uint64_t)fileSize / 512
321 || extendSize == 0
322 || (uint64_t)fileSize / 512 - extendOffset < extendSize) {
323 TRACE("Error: Invalid extend location!\n");
324 return B_BAD_DATA;
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,
334 extendSize * 512);
335 if (cookie == NULL)
336 return B_NO_MEMORY;
338 _cookie = cookie;
339 return B_OK;
343 // #pragma mark - module hooks
346 static status_t
347 vmdk_std_ops(int32 op, ...)
349 TRACE("vmdk_std_ops(0x%lx)\n", op);
350 switch(op) {
351 case B_MODULE_INIT:
352 case B_MODULE_UNINIT:
353 return B_OK;
355 return B_ERROR;
359 static float
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);
366 VmdkCookie* cookie;
367 status_t error = parse_vmdk_header(fd, partition->size, cookie);
368 if (error != B_OK)
369 return -1;
371 *_cookie = cookie;
372 return 0.8f;
376 static status_t
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;
394 // child
395 partition_data* child = create_child_partition(partition->id, 0,
396 partition->offset + cookie->contentOffset, cookie->contentSize, -1);
397 if (child == NULL) {
398 partition->content_cookie = NULL;
399 return B_ERROR;
402 child->block_size = partition->block_size;
403 // (no name)
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;
411 return B_NO_MEMORY;
414 cookieDeleter.Detach();
415 return B_OK;
419 static void
420 vmdk_free_identify_partition_cookie(partition_data*/* partition*/, void* cookie)
422 delete (VmdkCookie*)cookie;
426 static void
427 vmdk_free_partition_cookie(partition_data* partition)
429 // called for the child partition -- it doesn't have a cookie
433 static void
434 vmdk_free_partition_content_cookie(partition_data* partition)
436 delete (VmdkCookie*)partition->content_cookie;
440 #ifdef _BOOT_MODE
441 partition_module_info gVMwarePartitionModule =
442 #else
443 static partition_module_info vmdk_partition_module =
444 #endif
447 VMDK_PARTITION_MODULE_NAME,
449 vmdk_std_ops
451 "vmdk", // short_name
452 VMDK_PARTITION_NAME, // pretty_name
454 // flags
457 // scanning
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
464 #ifdef _BOOT_MODE
465 NULL
466 #endif // _BOOT_MODE
470 #ifndef _BOOT_MODE
471 extern "C" partition_module_info* modules[];
472 _EXPORT partition_module_info* modules[] =
474 &vmdk_partition_module,
475 NULL
477 #endif