- update sector count before calling write completion function (SF patch #2144692)
[bochs-mirror.git] / iodev / vmware3.cc
blob03ca2609551acb9cc1eb51251ef207d76a217a72
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: vmware3.cc,v 1.19 2008/01/26 22:24:03 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
5 /*
6 * This file provides support for VMWare's virtual disk image
7 * format version 3.
9 * Author: Sharvil Nanavati, for Net Integration Technologies, Inc.
10 * Contact: snrrrub@yahoo.com
12 * Copyright (C) 2003 Net Integration Technologies, Inc.
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 // Define BX_PLUGGABLE in files that can be compiled into plugins. For
30 // platforms that require a special tag on exported symbols, BX_PLUGGABLE
31 // is used to know when we are exporting symbols and when we are importing.
32 #define BX_PLUGGABLE
34 #define NO_DEVICE_INCLUDES
35 #include "iodev.h"
36 #include "hdimage.h"
37 #include "vmware3.h"
39 const off_t vmware3_image_t::INVALID_OFFSET=(off_t)-1;
40 #define LOG_THIS bx_devices.pluginHardDrive->
42 #define DTOH32_HEADER(field) (header.field = (dtoh32(header.field)))
43 #define HTOD32_HEADER(field) (header.field = (htod32(header.field)))
45 int vmware3_image_t::read_header(int fd, COW_Header & header)
47 int res;
49 if((res = ::read(fd, &header, sizeof(COW_Header))) < 0)
50 return res;
52 DTOH32_HEADER(header_version);
53 DTOH32_HEADER(flags);
54 DTOH32_HEADER(total_sectors);
55 DTOH32_HEADER(tlb_size_sectors);
56 DTOH32_HEADER(flb_offset_sectors);
57 DTOH32_HEADER(flb_count);
58 DTOH32_HEADER(next_sector_to_allocate);
59 DTOH32_HEADER(cylinders);
60 DTOH32_HEADER(heads);
61 DTOH32_HEADER(sectors);
62 DTOH32_HEADER(last_modified_time);
63 DTOH32_HEADER(last_modified_time_save);
64 DTOH32_HEADER(chain_id);
65 DTOH32_HEADER(number_of_chains);
66 DTOH32_HEADER(cylinders_in_disk);
67 DTOH32_HEADER(heads_in_disk);
68 DTOH32_HEADER(sectors_in_disk);
69 DTOH32_HEADER(total_sectors_in_disk);
70 DTOH32_HEADER(vmware_version);
72 return res;
75 int vmware3_image_t::write_header(int fd, COW_Header & hostHeader)
77 COW_Header header;
79 memcpy(&header, &hostHeader, sizeof(COW_Header));
81 HTOD32_HEADER(header_version);
82 HTOD32_HEADER(flags);
83 HTOD32_HEADER(total_sectors);
84 HTOD32_HEADER(tlb_size_sectors);
85 HTOD32_HEADER(flb_offset_sectors);
86 HTOD32_HEADER(flb_count);
87 HTOD32_HEADER(next_sector_to_allocate);
88 HTOD32_HEADER(cylinders);
89 HTOD32_HEADER(heads);
90 HTOD32_HEADER(sectors);
91 HTOD32_HEADER(last_modified_time);
92 HTOD32_HEADER(last_modified_time_save);
93 HTOD32_HEADER(chain_id);
94 HTOD32_HEADER(number_of_chains);
95 HTOD32_HEADER(cylinders_in_disk);
96 HTOD32_HEADER(heads_in_disk);
97 HTOD32_HEADER(sectors_in_disk);
98 HTOD32_HEADER(total_sectors_in_disk);
99 HTOD32_HEADER(vmware_version);
101 return ::write(fd, &header, sizeof(COW_Header));
104 #undef DTOH32_HEADER
105 #undef HTOD32_HEADER
107 int vmware3_image_t::read_ints(int fd, Bit32u *buffer, size_t count)
109 int res;
110 size_t i;
111 Bit32u *p;
113 res=::read(fd, (void*)buffer, count * 4);
114 for (p = buffer, i=0; i<count; p++, i++)
115 *p=dtoh32(*p);
117 return res;
120 int vmware3_image_t::write_ints(int fd, Bit32u *buffer, size_t count)
122 int res;
123 size_t i;
124 Bit32u *p;
126 for (p = buffer, i=0; i<count; p++, i++)
127 *p=htod32(*p);
129 res=::write(fd, (void*)buffer, count * 4);
131 for (p = buffer, i=0; i<count; p++, i++)
132 *p=dtoh32(*p);
134 return res;
137 bool vmware3_image_t::is_valid_header(COW_Header & header)
139 if(header.id[0] != 'C' || header.id[1] != 'O' || header.id[2] != 'W' ||
140 header.id[3] != 'D')
142 BX_DEBUG(("not a vmware3 COW disk"));
143 return false;
146 if(header.header_version != 3)
148 BX_DEBUG(("unsupported vmware3 COW disk header version"));
150 return false;
153 if(header.vmware_version != 2)
155 BX_DEBUG(("unsupported vmware3 COW disk version"));
156 return false;
159 return true;
162 char * vmware3_image_t::generate_cow_name(const char * filename, unsigned chain)
164 char * name = new char[strlen(filename) + 4];
165 if(name == 0)
166 BX_PANIC(("unable to allocate %lu bytes for vmware3 COW file name (base: %s, chain: %u)", strlen(filename) + 4, filename, chain));
167 strcpy(name, filename);
168 if(chain != 0)
170 char * period = strrchr(name, '.');
171 if (period != 0)
173 char temp[1024];
174 strcpy(temp, period + 1);
175 *period = 0;
176 sprintf(name, "%s-%02d.%s", name, chain + 1, temp);
178 else
179 sprintf(name, "%s-%02d", name, chain + 1);
181 return name;
185 * This function will panic if errors occur when attempting to open an image
186 * file. Now if only I could use exceptions to handle the errors in an elegant
187 * fashion...
189 int vmware3_image_t::open(const char * pathname)
191 COW_Header header;
192 int file;
193 int flags = O_RDWR;
194 #ifdef O_BINARY
195 flags |= O_BINARY;
196 #endif
198 // Set so close doesn't segfault, in case something goes wrong
199 images = NULL;
201 /* Open the virtual disk */
202 file = ::open(pathname, flags);
204 if(file < 0)
205 return -1;
207 /* Read the header */
208 if(read_header(file, header) < 0)
209 BX_PANIC(("unable to read vmware3 COW Disk header from file '%s'", pathname));
211 /* Make sure it's a valid header */
212 if(!is_valid_header(header))
213 BX_PANIC(("invalid vmware3 COW Disk image"));
215 ::close(file);
217 tlb_size = header.tlb_size_sectors * 512;
218 slb_count = (1 << FL_SHIFT) / tlb_size;
220 // we must have at least one chain
221 unsigned count = header.number_of_chains;
222 if (count < 1) count = 1;
224 images = new COW_Image [count];
226 off_t offset = 0;
227 for (unsigned i = 0; i < count; ++i)
229 char * filename = generate_cow_name(pathname, i);
230 current = &images[i];
232 current->fd = ::open(filename, flags);
233 if(current->fd < 0)
234 BX_PANIC(("unable to open vmware3 COW Disk file '%s'", filename));
236 if(read_header(current->fd, current->header) < 0)
237 BX_PANIC(("unable to read header or invalid header in vmware3 COW Disk file '%s'", filename));
239 if(!is_valid_header(current->header))
240 BX_PANIC(("invalid vmware3 COW Disk file '%s'", filename));
242 current->flb = new unsigned [current->header.flb_count];
243 if(current->flb == 0)
244 BX_PANIC(("cannot allocate %d bytes for flb in vmware3 COW Disk '%s'", current->header.flb_count * 4, filename));
246 current->slb = new unsigned * [current->header.flb_count];
247 if(current->slb == 0)
248 BX_PANIC(("cannot allocate %d bytes for slb in vmware3 COW Disk '%s'", current->header.flb_count * 4, filename));
250 unsigned j;
251 for(j = 0; j < current->header.flb_count; ++j)
253 current->slb[j] = new unsigned [slb_count];
254 if(current->slb[j] == 0)
255 BX_PANIC(("cannot allocate %d bytes for slb[] in vmware3 COW Disk '%s'", slb_count * 4, filename));
258 current->tlb = new Bit8u [tlb_size];
259 if(current->tlb == 0)
260 BX_PANIC(("cannot allocate %d bytes for tlb in vmware3 COW Disk '%s'", tlb_size, filename));
262 if(::lseek(current->fd, current->header.flb_offset_sectors * 512, SEEK_SET) < 0)
263 BX_PANIC(("unable to seek vmware3 COW Disk file '%s'", filename));
265 if(read_ints(current->fd, current->flb, current->header.flb_count) < 0)
266 BX_PANIC(("unable to read flb from vmware3 COW Disk file '%s'", filename));
268 for(j = 0; j < current->header.flb_count; ++j)
269 if(current->flb[j] != 0)
271 if(::lseek(current->fd, current->flb[j] * 512, SEEK_SET) < 0)
272 BX_PANIC(("unable to seek vmware3 COW Disk file '%s'", filename));
273 if(read_ints(current->fd, current->slb[j], slb_count) < 0)
274 BX_PANIC(("unable to read slb from vmware3 COW Disk file '%s'", filename));
277 current->min_offset = offset;
278 offset += current->header.total_sectors * 512;
279 current->max_offset = offset;
281 current->offset = INVALID_OFFSET;
282 current->synced = true;
283 delete[] filename;
285 current = &images[0];
286 requested_offset = 0;
287 if (header.total_sectors_in_disk!=0) {
288 cylinders = header.cylinders_in_disk;
289 heads = header.heads_in_disk;
290 sectors = header.sectors_in_disk;
291 hd_size = header.total_sectors_in_disk * 512;
292 } else {
293 cylinders = header.cylinders;
294 heads = header.heads;
295 sectors = header.sectors;
296 hd_size = header.total_sectors * 512;
299 return 1;
302 off_t vmware3_image_t::perform_seek()
304 if(requested_offset < current->min_offset || requested_offset >= current->max_offset)
306 if(!sync())
308 BX_DEBUG(("could not sync before switching vmware3 COW files"));
309 return INVALID_OFFSET;
312 while(requested_offset < current->min_offset)
313 current = &images[current->header.chain_id - 1];
315 while(requested_offset >= current->max_offset)
316 current = &images[current->header.chain_id + 1];
319 if(current->offset != INVALID_OFFSET && requested_offset >= current->offset
320 && requested_offset < current->offset + tlb_size)
321 return (requested_offset - current->offset);
323 if(!sync())
325 BX_DEBUG(("could not sync before seeking vmware3 COW file"));
326 return INVALID_OFFSET;
329 unsigned relative_offset = (unsigned)(requested_offset - current->min_offset);
330 unsigned i = relative_offset >> FL_SHIFT;
331 unsigned j = (relative_offset & ~FL_MASK) / tlb_size;
333 if(current->slb[i][j])
335 if(::lseek(current->fd, current->slb[i][j] * 512, SEEK_SET) < 0)
337 BX_DEBUG(("could not seek vmware3 COW to sector slb[%d][%d]", i, j));
338 return INVALID_OFFSET;
340 if(::read(current->fd, current->tlb, tlb_size) < 0)
342 BX_DEBUG(("could not read %d bytes from vmware3 COW image", tlb_size));
343 return INVALID_OFFSET;
346 else
347 memset(current->tlb, 0, tlb_size);
349 current->offset = (requested_offset / tlb_size) * tlb_size;
350 return (requested_offset - current->offset);
353 ssize_t vmware3_image_t::read(void * buf, size_t count)
355 ssize_t total = 0;
356 while(count > 0)
358 off_t offset = perform_seek();
359 if(offset == INVALID_OFFSET)
361 BX_DEBUG(("vmware3 COW read failed on %lu bytes", count));
362 return -1;
364 unsigned bytes_remaining = (unsigned)(tlb_size - offset);
365 unsigned amount = (bytes_remaining > count) ? count : bytes_remaining;
366 memcpy(buf, current->tlb + offset, amount);
367 requested_offset += amount;
368 total += amount;
369 count -= amount;
371 return total;
374 /* This could be done much better, I'm sure. In fact, the whole header doesn't
375 * need to be re-written each time a new tlb is allocated nor does the whole
376 * slb need to be re-written (most of the time) but that can be changed whenever
377 * it becomes an issue... image I/O is not a bottleneck.
379 bool vmware3_image_t::sync()
381 if(current->synced)
382 return true;
384 unsigned relative_offset = (unsigned)(current->offset - current->min_offset);
385 unsigned i = relative_offset >> FL_SHIFT;
386 unsigned j = (relative_offset & ~FL_MASK) / tlb_size;
388 if (current->slb[i][j] == 0)
390 if (current->flb[i] == 0)
392 unsigned slb_size = slb_count * 4;
394 /* Re-write the FLB */
395 current->flb[i] = current->header.next_sector_to_allocate;
396 if(::lseek(current->fd, current->header.flb_offset_sectors * 512, SEEK_SET) < 0)
398 BX_DEBUG(("could not seek vmware3 COW image to flb on sync"));
399 return false;
401 if(write_ints(current->fd, current->flb, current->header.flb_count) < 0)
403 BX_DEBUG(("could not re-write flb to vmware3 COW image on sync"));
404 return false;
406 current->header.next_sector_to_allocate += (slb_size / 512) + ((slb_size % 512) ? 1 : 0);
409 /* Re-write the SLB */
410 current->slb[i][j] = current->header.next_sector_to_allocate;
411 if(::lseek(current->fd, current->flb[i] * 512, SEEK_SET) < 0)
413 BX_DEBUG(("could not seek vmware3 COW image to slb on sync"));
414 return false;
416 if(write_ints(current->fd, current->slb[i], slb_count) < 0)
418 BX_DEBUG(("could not re-write slb to vmware3 COW image on sync"));
419 return false;
421 current->header.next_sector_to_allocate += current->header.tlb_size_sectors;
423 /* Update the header */
424 if(::lseek(current->fd, 0, SEEK_SET) < 0)
426 BX_DEBUG(("could not seek to vmware3 COW image to offset 0 on sync"));
427 return false;
429 if(write_header(current->fd, current->header) < 0)
431 BX_DEBUG(("could not re-write header to vmware3 COW image on sync"));
432 return false;
435 if(::lseek(current->fd, current->slb[i][j] * 512, SEEK_SET) < 0)
437 BX_DEBUG(("could not seek vmware3 COW image to offset %d on sync", current->slb[i][j] * 512));
438 return false;
440 if(::write(current->fd, current->tlb, tlb_size) < 0)
442 BX_DEBUG(("could not write tlb to vmware3 COW image on sync"));
443 return false;
445 current->synced = true;
446 return true;
449 ssize_t vmware3_image_t::write(const void * buf, size_t count)
451 ssize_t total = 0;
452 while(count > 0)
454 off_t offset = perform_seek();
455 if(offset == INVALID_OFFSET)
456 return -1;
457 unsigned bytes_remaining = (unsigned)(tlb_size - offset);
458 unsigned amount = 0;
459 current->synced = false;
460 if(bytes_remaining > count)
462 memcpy(current->tlb + offset, buf, count);
463 amount = count;
465 else
467 memcpy(current->tlb + offset, buf, bytes_remaining);
468 if(!sync())
470 BX_DEBUG(("failed to sync when writing %lu bytes", count));
471 return -1;
473 amount = bytes_remaining;
475 requested_offset += amount;
476 total += amount;
477 count -= amount;
479 return total;
482 Bit64s vmware3_image_t::lseek(Bit64s offset, int whence)
484 if(whence == SEEK_SET)
485 requested_offset = (off_t)offset;
486 else if (whence == SEEK_CUR)
487 requested_offset += (off_t)offset;
488 else if (whence == SEEK_END)
489 requested_offset = (off_t)(current->header.total_sectors_in_disk * 512) + (off_t)offset;
490 else
492 BX_DEBUG(("unknown 'whence' value (%d) when trying to seek vmware3 COW image", whence));
493 return -1;
495 return requested_offset;
498 void vmware3_image_t::close()
500 if(current == 0)
501 return;
503 unsigned count = current->header.number_of_chains;
504 if (count < 1) count = 1;
505 for(unsigned i = 0; i < count; ++i)
507 if (images != NULL)
509 current = &images[i];
510 for(unsigned j = 0; j < current->header.flb_count; ++j)
511 delete[] current->slb[j];
512 delete[] current->flb;
513 delete[] current->slb;
514 delete[] current->tlb;
515 ::close(current->fd);
516 delete[] images;
517 images = NULL;
520 current = 0;