1 /////////////////////////////////////////////////////////////////////////
2 // $Id: vmware4.cc,v 1.3 2007/10/24 23:17:42 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
6 * This file provides support for VMWare's virtual disk image
7 * format version 4 and above.
9 * Author: Sharvil Nanavati
10 * Contact: snrrrub@gmail.com
12 * Copyright (C) 2006 Sharvil Nanavati.
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
40 #define LOG_THIS bx_devices.pluginHardDrive->
41 const off_t
vmware4_image_t::INVALID_OFFSET
= (off_t
)-1;
42 const int vmware4_image_t::SECTOR_SIZE
= 512;
44 vmware4_image_t::vmware4_image_t()
45 : file_descriptor(-1),
47 tlb_offset(INVALID_OFFSET
),
48 current_offset(INVALID_OFFSET
),
53 vmware4_image_t::~vmware4_image_t()
58 int vmware4_image_t::open(const char * pathname
)
67 file_descriptor
= ::open(pathname
, flags
);
73 BX_PANIC(("unable to read vmware4 virtual disk header from file '%s'", pathname
));
75 tlb
= new Bit8u
[(unsigned)header
.tlb_size_sectors
* SECTOR_SIZE
];
77 BX_PANIC(("unable to allocate " FMT_LL
"d bytes for vmware4 image's tlb", header
.tlb_size_sectors
* SECTOR_SIZE
));
79 tlb_offset
= INVALID_OFFSET
;
83 hd_size
= header
.total_sectors
* SECTOR_SIZE
;
84 cylinders
= (unsigned)hd_size
/ (16 * 63);
88 BX_DEBUG(("VMware 4 disk geometry:"));
89 BX_DEBUG((" .size = " FMT_LL
"d", hd_size
));
90 BX_DEBUG((" .cylinders = %d", cylinders
));
91 BX_DEBUG((" .heads = %d", heads
));
92 BX_DEBUG((" .sectors = %d", sectors
));
97 void vmware4_image_t::close()
99 if(file_descriptor
== -1)
103 delete [] tlb
; tlb
= 0;
105 ::close(file_descriptor
);
106 file_descriptor
= -1;
109 Bit64s
vmware4_image_t::lseek(Bit64s offset
, int whence
)
114 current_offset
= (off_t
)offset
;
115 return current_offset
;
117 current_offset
+= (off_t
)offset
;
118 return current_offset
;
120 current_offset
= header
.total_sectors
* SECTOR_SIZE
+ (off_t
)offset
;
121 return current_offset
;
123 BX_DEBUG(("unknown 'whence' value (%d) when trying to seek vmware4 image", whence
));
124 return INVALID_OFFSET
;
128 ssize_t
vmware4_image_t::read(void * buf
, size_t count
)
133 off_t readable
= perform_seek();
134 if(readable
== INVALID_OFFSET
)
136 BX_DEBUG(("vmware4 disk image read failed on %d bytes at " FMT_LL
"d", count
, current_offset
));
140 off_t copysize
= (count
> readable
) ? readable
: count
;
141 memcpy(buf
, tlb
+ current_offset
- tlb_offset
, (size_t)copysize
);
143 current_offset
+= copysize
;
144 total
+= (long)copysize
;
145 count
-= (size_t)copysize
;
150 ssize_t
vmware4_image_t::write(const void * buf
, size_t count
)
155 off_t writable
= perform_seek();
156 if(writable
== INVALID_OFFSET
)
158 BX_DEBUG(("vmware4 disk image write failed on %d bytes at " FMT_LL
"d", count
, current_offset
));
162 off_t writesize
= (count
> writable
) ? writable
: count
;
163 memcpy(tlb
+ current_offset
- tlb_offset
, buf
, (size_t)writesize
);
165 current_offset
+= writesize
;
166 total
+= (long)writesize
;
167 count
-= (size_t)writesize
;
173 bool vmware4_image_t::is_open() const
175 return (file_descriptor
!= -1);
178 bool vmware4_image_t::is_valid_header() const
180 if(header
.id
[0] != 'K' || header
.id
[1] != 'D' || header
.id
[2] != 'M' ||
183 BX_DEBUG(("not a vmware4 image"));
187 if(header
.version
!= 1)
189 BX_DEBUG(("unsupported vmware4 image version"));
196 bool vmware4_image_t::read_header()
199 BX_PANIC(("attempt to read vmware4 header from a closed file"));
201 if(::read(file_descriptor
, &header
, sizeof(VM4_Header
)) != sizeof(VM4_Header
))
204 header
.version
= dtoh32(header
.version
);
205 header
.flags
= dtoh32(header
.flags
);
206 header
.total_sectors
= dtoh64(header
.total_sectors
);
207 header
.tlb_size_sectors
= dtoh64(header
.tlb_size_sectors
);
208 header
.description_offset_sectors
= dtoh64(header
.description_offset_sectors
);
209 header
.description_size_sectors
= dtoh64(header
.description_size_sectors
);
210 header
.slb_count
= dtoh32(header
.slb_count
);
211 header
.flb_offset_sectors
= dtoh64(header
.flb_offset_sectors
);
212 header
.flb_copy_offset_sectors
= dtoh64(header
.flb_copy_offset_sectors
);
213 header
.tlb_offset_sectors
= dtoh64(header
.tlb_offset_sectors
);
215 if(!is_valid_header())
216 BX_PANIC(("invalid vmware4 virtual disk image"));
218 BX_DEBUG(("VM4_Header (size=%d)", sizeof(VM4_Header
)));
219 BX_DEBUG((" .version = %d", header
.version
));
220 BX_DEBUG((" .flags = %d", header
.flags
));
221 BX_DEBUG((" .total_sectors = " FMT_LL
"d", header
.total_sectors
));
222 BX_DEBUG((" .tlb_size_sectors = " FMT_LL
"d", header
.tlb_size_sectors
));
223 BX_DEBUG((" .description_offset_sectors = " FMT_LL
"d", header
.description_offset_sectors
));
224 BX_DEBUG((" .description_size_sectors = " FMT_LL
"d", header
.description_size_sectors
));
225 BX_DEBUG((" .slb_count = %d", header
.slb_count
));
226 BX_DEBUG((" .flb_offset_sectors = " FMT_LL
"d", header
.flb_offset_sectors
));
227 BX_DEBUG((" .flb_copy_offset_sectors = " FMT_LL
"d", header
.flb_copy_offset_sectors
));
228 BX_DEBUG((" .tlb_offset_sectors = " FMT_LL
"d", header
.tlb_offset_sectors
));
234 // Returns the number of bytes that can be read from the current offset before needing
235 // to perform another seek.
237 off_t
vmware4_image_t::perform_seek()
239 if(current_offset
== INVALID_OFFSET
)
241 BX_DEBUG(("invalid offset specified in vmware4 seek"));
242 return INVALID_OFFSET
;
246 // The currently loaded tlb can service the request.
248 if(tlb_offset
/ (header
.tlb_size_sectors
* SECTOR_SIZE
) == current_offset
/ (header
.tlb_size_sectors
* SECTOR_SIZE
))
249 return (header
.tlb_size_sectors
* SECTOR_SIZE
) - (current_offset
- tlb_offset
);
253 Bit64u index
= current_offset
/ (header
.tlb_size_sectors
* SECTOR_SIZE
);
254 Bit32u slb_index
= (Bit32u
)(index
% header
.slb_count
);
255 Bit32u flb_index
= (Bit32u
)(index
/ header
.slb_count
);
257 Bit32u slb_sector
= read_block_index(header
.flb_offset_sectors
, flb_index
);
258 Bit32u slb_copy_sector
= read_block_index(header
.flb_copy_offset_sectors
, flb_index
);
260 if(slb_sector
== 0 && slb_copy_sector
== 0)
262 BX_DEBUG(("loaded vmware4 disk image requires un-implemented feature"));
263 return INVALID_OFFSET
;
266 slb_sector
= slb_copy_sector
;
268 Bit32u tlb_sector
= read_block_index(slb_sector
, slb_index
);
269 tlb_offset
= index
* header
.tlb_size_sectors
* SECTOR_SIZE
;
273 // Allocate a new tlb
275 memset(tlb
, 0, (size_t)header
.tlb_size_sectors
* SECTOR_SIZE
);
278 // Instead of doing a write to increase the file size, we could use
279 // ftruncate but it is not portable.
281 off_t eof
= ((::lseek(file_descriptor
, 0, SEEK_END
) + SECTOR_SIZE
- 1) / SECTOR_SIZE
) * SECTOR_SIZE
;
282 ::write(file_descriptor
, tlb
, (unsigned)header
.tlb_size_sectors
* SECTOR_SIZE
);
283 tlb_sector
= (Bit32u
)eof
/ SECTOR_SIZE
;
285 write_block_index(slb_sector
, slb_index
, tlb_sector
);
286 write_block_index(slb_copy_sector
, slb_index
, tlb_sector
);
288 ::lseek(file_descriptor
, eof
, SEEK_SET
);
292 ::lseek(file_descriptor
, tlb_sector
* SECTOR_SIZE
, SEEK_SET
);
293 ::read(file_descriptor
, tlb
, (unsigned)header
.tlb_size_sectors
* SECTOR_SIZE
);
294 ::lseek(file_descriptor
, tlb_sector
* SECTOR_SIZE
, SEEK_SET
);
297 return (header
.tlb_size_sectors
* SECTOR_SIZE
) - (current_offset
- tlb_offset
);
300 void vmware4_image_t::flush()
306 // Write dirty sectors to disk first. Assume that the file is already at the
307 // position for the current tlb.
309 ::write(file_descriptor
, tlb
, (unsigned)header
.tlb_size_sectors
* SECTOR_SIZE
);
313 Bit32u
vmware4_image_t::read_block_index(Bit64u sector
, Bit32u index
)
317 ::lseek(file_descriptor
, sector
* SECTOR_SIZE
+ index
* sizeof(Bit32u
), SEEK_SET
);
318 ::read(file_descriptor
, &ret
, sizeof(Bit32u
));
323 void vmware4_image_t::write_block_index(Bit64u sector
, Bit32u index
, Bit32u block_sector
)
325 block_sector
= htod32(block_sector
);
327 ::lseek(file_descriptor
, sector
* SECTOR_SIZE
+ index
* sizeof(Bit32u
), SEEK_SET
);
328 ::write(file_descriptor
, &block_sector
, sizeof(Bit32u
));