1 /////////////////////////////////////////////////////////////////////////
2 // $Id: vmware3.cc,v 1.19 2008/01/26 22:24:03 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
6 * This file provides support for VMWare's virtual disk image
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.
34 #define NO_DEVICE_INCLUDES
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
)
49 if((res
= ::read(fd
, &header
, sizeof(COW_Header
))) < 0)
52 DTOH32_HEADER(header_version
);
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
);
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
);
75 int vmware3_image_t::write_header(int fd
, COW_Header
& hostHeader
)
79 memcpy(&header
, &hostHeader
, sizeof(COW_Header
));
81 HTOD32_HEADER(header_version
);
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
);
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
));
107 int vmware3_image_t::read_ints(int fd
, Bit32u
*buffer
, size_t count
)
113 res
=::read(fd
, (void*)buffer
, count
* 4);
114 for (p
= buffer
, i
=0; i
<count
; p
++, i
++)
120 int vmware3_image_t::write_ints(int fd
, Bit32u
*buffer
, size_t count
)
126 for (p
= buffer
, i
=0; i
<count
; p
++, i
++)
129 res
=::write(fd
, (void*)buffer
, count
* 4);
131 for (p
= buffer
, i
=0; i
<count
; p
++, i
++)
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' ||
142 BX_DEBUG(("not a vmware3 COW disk"));
146 if(header
.header_version
!= 3)
148 BX_DEBUG(("unsupported vmware3 COW disk header version"));
153 if(header
.vmware_version
!= 2)
155 BX_DEBUG(("unsupported vmware3 COW disk version"));
162 char * vmware3_image_t::generate_cow_name(const char * filename
, unsigned chain
)
164 char * name
= new char[strlen(filename
) + 4];
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
);
170 char * period
= strrchr(name
, '.');
174 strcpy(temp
, period
+ 1);
176 sprintf(name
, "%s-%02d.%s", name
, chain
+ 1, temp
);
179 sprintf(name
, "%s-%02d", name
, chain
+ 1);
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
189 int vmware3_image_t::open(const char * pathname
)
198 // Set so close doesn't segfault, in case something goes wrong
201 /* Open the virtual disk */
202 file
= ::open(pathname
, flags
);
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"));
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
];
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
);
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
));
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;
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;
293 cylinders
= header
.cylinders
;
294 heads
= header
.heads
;
295 sectors
= header
.sectors
;
296 hd_size
= header
.total_sectors
* 512;
302 off_t
vmware3_image_t::perform_seek()
304 if(requested_offset
< current
->min_offset
|| requested_offset
>= current
->max_offset
)
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
);
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
;
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
)
358 off_t offset
= perform_seek();
359 if(offset
== INVALID_OFFSET
)
361 BX_DEBUG(("vmware3 COW read failed on %lu bytes", count
));
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
;
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()
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"));
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"));
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"));
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"));
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"));
429 if(write_header(current
->fd
, current
->header
) < 0)
431 BX_DEBUG(("could not re-write header to vmware3 COW image on sync"));
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));
440 if(::write(current
->fd
, current
->tlb
, tlb_size
) < 0)
442 BX_DEBUG(("could not write tlb to vmware3 COW image on sync"));
445 current
->synced
= true;
449 ssize_t
vmware3_image_t::write(const void * buf
, size_t count
)
454 off_t offset
= perform_seek();
455 if(offset
== INVALID_OFFSET
)
457 unsigned bytes_remaining
= (unsigned)(tlb_size
- offset
);
459 current
->synced
= false;
460 if(bytes_remaining
> count
)
462 memcpy(current
->tlb
+ offset
, buf
, count
);
467 memcpy(current
->tlb
+ offset
, buf
, bytes_remaining
);
470 BX_DEBUG(("failed to sync when writing %lu bytes", count
));
473 amount
= bytes_remaining
;
475 requested_offset
+= amount
;
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
;
492 BX_DEBUG(("unknown 'whence' value (%d) when trying to seek vmware3 COW image", whence
));
495 return requested_offset
;
498 void vmware3_image_t::close()
503 unsigned count
= current
->header
.number_of_chains
;
504 if (count
< 1) count
= 1;
505 for(unsigned i
= 0; i
< count
; ++i
)
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
);