DOSBox-X
|
00001 /* 00002 * 00003 * Copyright (c) 2018 Shane Krueger 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License as published by 00007 * the Free Software Foundation; either version 2 of the License, or 00008 * (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License along 00016 * with this program; if not, write to the Free Software Foundation, Inc., 00017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "dosbox.h" 00021 #include "callback.h" 00022 #include "bios.h" 00023 #include "bios_disk.h" 00024 #include "regs.h" 00025 #include "mem.h" 00026 #include "dos_inc.h" /* for Drives[] */ 00027 #include "../dos/drives.h" 00028 #include "mapper.h" 00029 #include "SDL.h" 00030 00031 /* 00032 * imageDiskVHD supports fixed, dynamic, and differential VHD file formats 00033 * 00034 * Public members: Open(), GetVHDType() 00035 * 00036 * Notes: 00037 * - create instance using Open() 00038 * - code assumes that c/h/s values stored within VHD file are accurate, and does not scan the MBR 00039 * - VHDs with 511 byte footers are not yet supported 00040 * - VHDX files are not supported 00041 * - checksum values are verified; and the backup header is used if the footer cannot be found or has an invalid checksum 00042 * - code does not prevent loading if the VHD is marked as being part of a saved state 00043 * - code prevents loading if parent does not match correct guid 00044 * - code does not prevent loading if parent does not match correct datestamp 00045 * - for differencing disks, parent paths are converted to ASCII; unicode characters will cancel loading 00046 * - differencing disks only support absolute paths on Windows platforms 00047 * 00048 */ 00049 00050 imageDiskVHD::ErrorCodes imageDiskVHD::Open(const char* fileName, const bool readOnly, imageDisk** disk) { 00051 return Open(fileName, readOnly, disk, 0); 00052 } 00053 00054 imageDiskVHD::ErrorCodes imageDiskVHD::Open(const char* fileName, const bool readOnly, imageDisk** disk, const Bit8u* matchUniqueId) { 00055 //validate input parameters 00056 if (fileName == NULL) return ERROR_OPENING; 00057 if (disk == NULL) return ERROR_OPENING; 00058 //verify that C++ didn't add padding to the structure layout 00059 assert(sizeof(VHDFooter) == 512); 00060 assert(sizeof(DynamicHeader) == 1024); 00061 //open file and clear C++ buffering 00062 FILE* file = fopen64(fileName, readOnly ? "rb" : "rb+"); 00063 if (!file) return ERROR_OPENING; 00064 setbuf(file, NULL); 00065 //check that length of file is > 512 bytes 00066 if (fseeko64(file, 0L, SEEK_END)) { fclose(file); return INVALID_DATA; } 00067 if (ftello64(file) < 512) { fclose(file); return INVALID_DATA; } 00068 //read footer 00069 if (fseeko64(file, -512L, SEEK_CUR)) { fclose(file); return INVALID_DATA; } 00070 Bit64u footerPosition = (Bit64u)((Bit64s)ftello64(file)); /* make sure to sign extend! */ 00071 if ((Bit64s)footerPosition < 0LL) { fclose(file); return INVALID_DATA; } 00072 VHDFooter originalfooter; 00073 VHDFooter footer; 00074 if (fread(&originalfooter, 512, 1, file) != 1) { fclose(file); return INVALID_DATA; } 00075 //convert from big-endian if necessary 00076 footer = originalfooter; 00077 footer.SwapByteOrder(); 00078 //verify checksum on footer 00079 if (!footer.IsValid()) { 00080 //if invalid, read header, and verify checksum on header 00081 if (fseeko64(file, 0L, SEEK_SET)) { fclose(file); return INVALID_DATA; } 00082 if (fread(&originalfooter, sizeof(Bit8u), 512, file) != 512) { fclose(file); return INVALID_DATA; } 00083 //convert from big-endian if necessary 00084 footer = originalfooter; 00085 footer.SwapByteOrder(); 00086 //verify checksum on header 00087 if (!footer.IsValid()) { fclose(file); return INVALID_DATA; } 00088 } 00089 //check that uniqueId matches 00090 if (matchUniqueId && memcmp(matchUniqueId, footer.uniqueId, 16)) { fclose(file); return INVALID_MATCH; } 00091 //calculate disk size 00092 Bit64u calcDiskSize = (Bit64u)footer.geometry.cylinders * (Bit64u)footer.geometry.heads * (Bit64u)footer.geometry.sectors * (Bit64u)512; 00093 if (!calcDiskSize) { fclose(file); return INVALID_DATA; } 00094 //if fixed image, return plain imageDisk rather than imageDiskVFD 00095 if (footer.diskType == VHD_TYPE_FIXED) { 00096 //make sure that the image size is at least as big as the geometry 00097 if (calcDiskSize > footerPosition) { 00098 fclose(file); 00099 return INVALID_DATA; 00100 } 00101 *disk = new imageDisk(file, fileName, footer.geometry.cylinders, footer.geometry.heads, footer.geometry.sectors, 512, true); 00102 return OPEN_SUCCESS; 00103 } 00104 00105 //set up imageDiskVHD here 00106 imageDiskVHD* vhd = new imageDiskVHD(); 00107 vhd->footerPosition = footerPosition; 00108 vhd->footer = footer; 00109 vhd->vhdType = footer.diskType; 00110 vhd->originalFooter = originalfooter; 00111 vhd->cylinders = footer.geometry.cylinders; 00112 vhd->heads = footer.geometry.heads; 00113 vhd->sectors = footer.geometry.sectors; 00114 vhd->sector_size = 512; 00115 vhd->diskSizeK = calcDiskSize / 1024; 00116 vhd->diskimg = file; 00117 vhd->diskname = fileName; 00118 vhd->hardDrive = true; 00119 vhd->active = true; 00120 //use delete vhd from now on to release the disk image upon failure 00121 00122 //if not dynamic or differencing, fail 00123 if (footer.diskType != VHD_TYPE_DYNAMIC && footer.diskType != VHD_TYPE_DIFFERENCING) { 00124 delete vhd; 00125 return UNSUPPORTED_TYPE; 00126 } 00127 //read dynamic disk header (applicable for dynamic and differencing types) 00128 DynamicHeader dynHeader; 00129 if (fseeko64(file, (off_t)footer.dataOffset, SEEK_SET)) { delete vhd; return INVALID_DATA; } 00130 if (fread(&dynHeader, sizeof(Bit8u), 1024, file) != 1024) { delete vhd; return INVALID_DATA; } 00131 //swap byte order and validate checksum 00132 dynHeader.SwapByteOrder(); 00133 if (!dynHeader.IsValid()) { delete vhd; return INVALID_DATA; } 00134 //check block size is valid (should be a power of 2, and at least equal to one sector) 00135 if (dynHeader.blockSize < 512 || (dynHeader.blockSize & (dynHeader.blockSize - 1))) { delete vhd; return INVALID_DATA; } 00136 00137 //if differencing, try to open parent 00138 if (footer.diskType == VHD_TYPE_DIFFERENCING) { 00139 00140 //loop through each reference and try to find one we can open 00141 for (int i = 0; i < 8; i++) { 00142 //ignore entries with platform code 'none' 00143 if (dynHeader.parentLocatorEntry[i].platformCode != 0) { 00144 //load the platform data, if there is any 00145 Bit64u dataOffset = dynHeader.parentLocatorEntry[i].platformDataOffset; 00146 Bit32u dataLength = dynHeader.parentLocatorEntry[i].platformDataLength; 00147 Bit8u* buffer = 0; 00148 if (dataOffset && dataLength && ((Bit64u)dataOffset + dataLength) <= footerPosition) { 00149 if (fseeko64(file, (off_t)dataOffset, SEEK_SET)) { delete vhd; return INVALID_DATA; } 00150 buffer = (Bit8u*)malloc(dataLength + 2); 00151 if (buffer == 0) { delete vhd; return INVALID_DATA; } 00152 if (fread(buffer, sizeof(Bit8u), dataLength, file) != dataLength) { free(buffer); delete vhd; return INVALID_DATA; } 00153 buffer[dataLength] = 0; //append null character, just in case 00154 buffer[dataLength + 1] = 0; //two bytes, because this might be UTF-16 00155 } 00156 ErrorCodes ret = TryOpenParent(fileName, dynHeader.parentLocatorEntry[i], buffer, buffer ? dataLength : 0, &(vhd->parentDisk), dynHeader.parentUniqueId); 00157 if (buffer) free(buffer); 00158 if (vhd->parentDisk) { //if successfully opened 00159 vhd->parentDisk->Addref(); 00160 break; 00161 } 00162 if (ret != ERROR_OPENING) { 00163 delete vhd; 00164 return (ErrorCodes)(ret | PARENT_ERROR); 00165 } 00166 } 00167 } 00168 //check and see if we were successful in opening a file 00169 if (vhd->parentDisk == 0) { delete vhd; return ERROR_OPENING_PARENT; } 00170 } 00171 00172 //calculate sectors per block 00173 Bit32u sectorsPerBlock = dynHeader.blockSize / 512; 00174 //calculate block map size 00175 Bit32u blockMapSectors = (( 00176 ((sectorsPerBlock /* 4096 sectors/block (typ) = 4096 bits required */ 00177 + 7) / 8) /* convert to bytes and round up to nearest byte; 4096/8 = 512 bytes required */ 00178 + 511) / 512); /* convert to sectors and round up to nearest sector; 512/512 = 1 sector */ 00179 //check that the BAT is large enough for the disk 00180 Bit32u tablesRequired = (Bit32u)((calcDiskSize + (dynHeader.blockSize - 1)) / dynHeader.blockSize); 00181 if (dynHeader.maxTableEntries < tablesRequired) { delete vhd; return INVALID_DATA; } 00182 //check that the BAT is contained within the file 00183 if (((Bit64u)dynHeader.tableOffset + ((Bit64u)dynHeader.maxTableEntries * (Bit64u)4)) > footerPosition) { delete vhd; return INVALID_DATA; } 00184 00185 //set remaining variables 00186 vhd->dynamicHeader = dynHeader; 00187 vhd->blockMapSectors = blockMapSectors; 00188 vhd->blockMapSize = blockMapSectors * 512; 00189 vhd->sectorsPerBlock = sectorsPerBlock; 00190 vhd->currentBlockDirtyMap = (Bit8u*)malloc(vhd->blockMapSize); 00191 if (vhd->currentBlockDirtyMap == 0) { delete vhd; return INVALID_DATA; } 00192 00193 //try loading the first block 00194 if (!vhd->loadBlock(0)) { 00195 delete vhd; 00196 return INVALID_DATA; 00197 } 00198 00199 *disk = vhd; 00200 return OPEN_SUCCESS; 00201 } 00202 00203 int iso8859_1_encode(int utf32code) { 00204 return ((utf32code >= 32 && utf32code <= 126) || (utf32code >= 160 && utf32code <= 255)) ? utf32code : -1; 00205 } 00206 00207 //this function (1) converts data from UTF-16 to a native string for fopen (depending on the host OS), and (2) converts slashes, if necessary 00208 bool imageDiskVHD::convert_UTF16_for_fopen(std::string &string, const void* data, const Bit32u dataLength) { 00209 //note: data is UTF-16 and always little-endian, with no null terminator 00210 //dataLength is not the number of characters, but the number of bytes 00211 00212 string.reserve((size_t)(string.size() + (dataLength / 2) + 10)); //allocate a few extra bytes 00213 char* indata = (char*)data; 00214 const char* lastchar = indata + dataLength; 00215 #if !defined (WIN32) && !defined(OS2) 00216 char temp[10]; 00217 char* tempout; 00218 char* tempout2; 00219 char* tempoutmax = &temp[10]; 00220 #endif 00221 while (indata < lastchar) { 00222 //decode the character 00223 int utf32code = utf16le_decode((const char**)&indata, lastchar); 00224 if (utf32code < 0) return false; 00225 #if defined (WIN32) || defined(OS2) 00226 //MSDN docs define fopen to accept strings in the windows default code page, which is typically Windows-1252 00227 //convert unicode string to ISO-8859, which is a subset of Windows-1252, and a lot easier to implement 00228 int iso8859_1code = iso8859_1_encode(utf32code); 00229 if (iso8859_1code < 0) return false; 00230 //and note that backslashes stay as backslashes on windows 00231 string += (char)iso8859_1code; 00232 #else 00233 //backslashes become slashes on Linux 00234 if (utf32code == '\\') utf32code = '/'; 00235 //Linux ext3 filenames are byte strings, typically in UTF-8 00236 //encode the character to a temporary buffer 00237 tempout = temp; 00238 if (utf8_encode(&tempout, tempoutmax, (unsigned int)utf32code) < 0) return false; 00239 //and append the byte(s) to the string 00240 tempout2 = temp; 00241 while (tempout2 < tempout) string += *tempout2++; 00242 #endif 00243 } 00244 return true; 00245 } 00246 00247 imageDiskVHD::ErrorCodes imageDiskVHD::TryOpenParent(const char* childFileName, const imageDiskVHD::ParentLocatorEntry& entry, const Bit8u* data, const Bit32u dataLength, imageDisk** disk, const Bit8u* uniqueId) { 00248 std::string str = ""; 00249 const char* slashpos = NULL; 00250 00251 switch (entry.platformCode) { 00252 case 0x57327275: 00253 //Unicode relative pathname (UTF-16) on Windows 00254 00255 #if defined (WIN32) || defined(OS2) 00256 /* Windows */ 00257 slashpos = strrchr(childFileName, '\\'); 00258 #else 00259 /* Linux */ 00260 slashpos = strrchr(childFileName, '/'); 00261 #endif 00262 if (slashpos != NULL) { 00263 //copy all characters up to and including the slash, to str 00264 for (const char* pos = (char*)childFileName; pos <= slashpos; pos++) { 00265 str += *pos; 00266 } 00267 } 00268 00269 //convert byte order, and UTF-16 to ASCII, and change backslashes to slashes if on Linux 00270 if (!convert_UTF16_for_fopen(str, data, dataLength)) break; 00271 00272 return imageDiskVHD::Open(str.c_str(), true, disk, uniqueId); 00273 00274 case 0x57326B75: 00275 //Unicode absolute pathname (UTF-16) on Windows 00276 00277 #if defined (WIN32) || defined(OS2) 00278 /* nothing */ 00279 #else 00280 // Linux 00281 // Todo: convert absolute pathname to something applicable for Linux 00282 break; 00283 #endif 00284 00285 //convert byte order, and UTF-16 to ASCII, and change backslashes to slashes if on Linux 00286 if (!convert_UTF16_for_fopen(str, data, dataLength)) break; 00287 00288 return imageDiskVHD::Open(str.c_str(), true, disk, uniqueId); 00289 00290 case 0x4D616320: 00291 //Mac OS alias stored as blob 00292 break; 00293 case 0x4D616358: 00294 //Mac OSX file url (RFC 2396) 00295 break; 00296 } 00297 return ERROR_OPENING; //return ERROR_OPENING if the file does not exist, cannot be accessed, etc 00298 } 00299 00300 Bit8u imageDiskVHD::Read_AbsoluteSector(Bit32u sectnum, void * data) { 00301 Bit32u blockNumber = sectnum / sectorsPerBlock; 00302 Bit32u sectorOffset = sectnum % sectorsPerBlock; 00303 if (!loadBlock(blockNumber)) return 0x05; //can't load block 00304 if (currentBlockAllocated) { 00305 Bit32u byteNum = sectorOffset / 8; 00306 Bit32u bitNum = sectorOffset % 8; 00307 bool hasData = currentBlockDirtyMap[byteNum] & (1 << (7 - bitNum)); 00308 if (hasData) { 00309 if (fseeko64(diskimg, (off_t)(((Bit64u)currentBlockSectorOffset + blockMapSectors + sectorOffset) * 512ull), SEEK_SET)) return 0x05; //can't seek 00310 if (fread(data, sizeof(Bit8u), 512, diskimg) != 512) return 0x05; //can't read 00311 return 0; 00312 } 00313 } 00314 if (parentDisk) { 00315 return parentDisk->Read_AbsoluteSector(sectnum, data); 00316 } 00317 else { 00318 memset(data, 0, 512); 00319 return 0; 00320 } 00321 } 00322 00323 Bit8u imageDiskVHD::Write_AbsoluteSector(Bit32u sectnum, const void * data) { 00324 Bit32u blockNumber = sectnum / sectorsPerBlock; 00325 Bit32u sectorOffset = sectnum % sectorsPerBlock; 00326 if (!loadBlock(blockNumber)) return 0x05; //can't load block 00327 if (!currentBlockAllocated) { 00328 if (!copiedFooter) { 00329 //write backup of footer at start of file (should already exist, but we never checked to be sure it is readable or matches the footer we used) 00330 if (fseeko64(diskimg, (off_t)0, SEEK_SET)) return 0x05; 00331 if (fwrite(&originalFooter, sizeof(Bit8u), 512, diskimg) != 512) return 0x05; 00332 copiedFooter = true; 00333 //flush the data to disk after writing the backup footer 00334 if (fflush(diskimg)) return 0x05; 00335 } 00336 //calculate new location of footer, and round up to nearest 512 byte increment "just in case" 00337 Bit64u newFooterPosition = (((footerPosition - 512ull + blockMapSize + dynamicHeader.blockSize) + 511ull) / 512ull) * 512ull; 00338 //attempt to extend the length appropriately first (on some operating systems this will extend the file) 00339 if (fseeko64(diskimg, (off_t)newFooterPosition + 512, SEEK_SET)) return 0x05; 00340 //now write the footer 00341 if (fseeko64(diskimg, (off_t)newFooterPosition, SEEK_SET)) return 0x05; 00342 if (fwrite(&originalFooter, sizeof(Bit8u), 512, diskimg) != 512) return 0x05; 00343 //save the new block location and new footer position 00344 Bit32u newBlockSectorNumber = (Bit32u)((footerPosition + 511ul) / 512ul); 00345 footerPosition = newFooterPosition; 00346 //clear the dirty flags for the new footer position 00347 for (Bit32u i = 0; i < blockMapSize; i++) currentBlockDirtyMap[i] = 0; 00348 //write the dirty map 00349 if (fseeko64(diskimg, (off_t)(newBlockSectorNumber * 512ull), SEEK_SET)) return 0x05; 00350 if (fwrite(currentBlockDirtyMap, sizeof(Bit8u), blockMapSize, diskimg) != blockMapSize) return 0x05; 00351 //flush the data to disk after expanding the file, before allocating the block in the BAT 00352 if (fflush(diskimg)) return 0x05; 00353 //update the BAT 00354 if (fseeko64(diskimg, (off_t)(dynamicHeader.tableOffset + (blockNumber * 4ull)), SEEK_SET)) return 0x05; 00355 Bit32u newBlockSectorNumberBE = SDL_SwapBE32(newBlockSectorNumber); 00356 if (fwrite(&newBlockSectorNumberBE, sizeof(Bit8u), 4, diskimg) != 4) return false; 00357 currentBlockAllocated = true; 00358 currentBlockSectorOffset = newBlockSectorNumber; 00359 //flush the data to disk after allocating a block 00360 if (fflush(diskimg)) return 0x05; 00361 } 00362 //current block has now been allocated 00363 Bit32u byteNum = sectorOffset / 8; 00364 Bit32u bitNum = sectorOffset % 8; 00365 bool hasData = currentBlockDirtyMap[byteNum] & (1 << (7 - bitNum)); 00366 //if the sector hasn't been marked as dirty, mark it as dirty 00367 if (!hasData) { 00368 currentBlockDirtyMap[byteNum] |= 1 << (7 - bitNum); 00369 if (fseeko64(diskimg, (off_t)(currentBlockSectorOffset * 512ull), SEEK_SET)) return 0x05; //can't seek 00370 if (fwrite(currentBlockDirtyMap, sizeof(Bit8u), blockMapSize, diskimg) != blockMapSize) return 0x05; 00371 } 00372 //current sector has now been marked as dirty 00373 //write the sector 00374 if (fseeko64(diskimg, (off_t)(((Bit64u)currentBlockSectorOffset + (Bit64u)blockMapSectors + (Bit64u)sectorOffset) * 512ull), SEEK_SET)) return 0x05; //can't seek 00375 if (fwrite(data, sizeof(Bit8u), 512, diskimg) != 512) return 0x05; //can't write 00376 return 0; 00377 } 00378 00379 imageDiskVHD::VHDTypes imageDiskVHD::GetVHDType(const char* fileName) { 00380 imageDisk* disk; 00381 if (Open(fileName, true, &disk)) return VHD_TYPE_NONE; 00382 const imageDiskVHD* vhd = dynamic_cast<imageDiskVHD*>(disk); 00383 VHDTypes ret = VHD_TYPE_FIXED; //fixed if an imageDisk was returned 00384 if (vhd) ret = vhd->footer.diskType; //get the actual type if an imageDiskVHD was returned 00385 delete disk; 00386 return ret; 00387 } 00388 00389 bool imageDiskVHD::loadBlock(const Bit32u blockNumber) { 00390 if (currentBlock == blockNumber) return true; 00391 if (blockNumber >= dynamicHeader.maxTableEntries) return false; 00392 if (fseeko64(diskimg, (off_t)(dynamicHeader.tableOffset + (blockNumber * 4ull)), SEEK_SET)) return false; 00393 Bit32u blockSectorOffset; 00394 if (fread(&blockSectorOffset, sizeof(Bit8u), 4, diskimg) != 4) return false; 00395 blockSectorOffset = SDL_SwapBE32(blockSectorOffset); 00396 if (blockSectorOffset == 0xFFFFFFFFul) { 00397 currentBlock = blockNumber; 00398 currentBlockAllocated = false; 00399 } 00400 else { 00401 if (fseeko64(diskimg, (off_t)(blockSectorOffset * (Bit64u)512), SEEK_SET)) return false; 00402 currentBlock = 0xFFFFFFFFul; 00403 currentBlockAllocated = true; 00404 currentBlockSectorOffset = blockSectorOffset; 00405 if (fread(currentBlockDirtyMap, sizeof(Bit8u), blockMapSize, diskimg) != blockMapSize) return false; 00406 currentBlock = blockNumber; 00407 } 00408 return true; 00409 } 00410 00411 imageDiskVHD::~imageDiskVHD() { 00412 if (currentBlockDirtyMap) { 00413 free(currentBlockDirtyMap); 00414 currentBlockDirtyMap = 0; 00415 } 00416 if (parentDisk) { 00417 parentDisk->Release(); 00418 parentDisk = 0; 00419 } 00420 } 00421 00422 void imageDiskVHD::VHDFooter::SwapByteOrder() { 00423 features = SDL_SwapBE32(features); 00424 fileFormatVersion = SDL_SwapBE32(fileFormatVersion); 00425 dataOffset = SDL_SwapBE64(dataOffset); 00426 timeStamp = SDL_SwapBE32(timeStamp); 00427 creatorVersion = SDL_SwapBE32(creatorVersion); 00428 creatorHostOS = SDL_SwapBE32(creatorHostOS); 00429 originalSize = SDL_SwapBE64(originalSize); 00430 currentSize = SDL_SwapBE64(currentSize); 00431 geometry.cylinders = SDL_SwapBE16(geometry.cylinders); 00432 diskType = (VHDTypes)SDL_SwapBE32((Bit32u)diskType); 00433 checksum = SDL_SwapBE32(checksum); 00434 //guid might need the byte order swapped also 00435 //however, for our purposes (comparing to the parent guid on differential disks), 00436 // it doesn't matter so long as we are consistent 00437 } 00438 00439 Bit32u imageDiskVHD::VHDFooter::CalculateChecksum() { 00440 //checksum is one's complement of sum of bytes excluding the checksum 00441 //because of that, the byte order doesn't matter when calculating the checksum 00442 //however, the checksum must be stored in the correct byte order or it will not match 00443 00444 Bit32u ret = 0; 00445 const Bit8u* dat = (Bit8u*)this->cookie; 00446 Bit32u oldChecksum = checksum; 00447 checksum = 0; 00448 for (size_t i = 0; i < sizeof(VHDFooter); i++) { 00449 ret += dat[i]; 00450 } 00451 checksum = oldChecksum; 00452 return ~ret; 00453 } 00454 00455 bool imageDiskVHD::VHDFooter::IsValid() { 00456 return ( 00457 memcmp(cookie, "conectix", 8) == 0 && 00458 fileFormatVersion >= 0x00010000 && 00459 fileFormatVersion <= 0x0001FFFF && 00460 checksum == CalculateChecksum()); 00461 } 00462 00463 void imageDiskVHD::DynamicHeader::SwapByteOrder() { 00464 dataOffset = SDL_SwapBE64(dataOffset); 00465 tableOffset = SDL_SwapBE64(tableOffset); 00466 headerVersion = SDL_SwapBE32(headerVersion); 00467 maxTableEntries = SDL_SwapBE32(maxTableEntries); 00468 blockSize = SDL_SwapBE32(blockSize); 00469 checksum = SDL_SwapBE32(checksum); 00470 parentTimeStamp = SDL_SwapBE32(parentTimeStamp); 00471 for (int i = 0; i < 256; i++) { 00472 parentUnicodeName[i] = SDL_SwapBE16(parentUnicodeName[i]); 00473 } 00474 for (int i = 0; i < 8; i++) { 00475 parentLocatorEntry[i].platformCode = SDL_SwapBE32(parentLocatorEntry[i].platformCode); 00476 parentLocatorEntry[i].platformDataSpace = SDL_SwapBE32(parentLocatorEntry[i].platformDataSpace); 00477 parentLocatorEntry[i].platformDataLength = SDL_SwapBE32(parentLocatorEntry[i].platformDataLength); 00478 parentLocatorEntry[i].reserved = SDL_SwapBE32(parentLocatorEntry[i].reserved); 00479 parentLocatorEntry[i].platformDataOffset = SDL_SwapBE64(parentLocatorEntry[i].platformDataOffset); 00480 } 00481 //parent guid might need the byte order swapped also 00482 //however, for our purposes (comparing to the parent guid on differential disks), 00483 // it doesn't matter so long as we are consistent 00484 } 00485 00486 Bit32u imageDiskVHD::DynamicHeader::CalculateChecksum() { 00487 //checksum is one's complement of sum of bytes excluding the checksum 00488 //because of that, the byte order doesn't matter when calculating the checksum 00489 //however, the checksum must be stored in the correct byte order or it will not match 00490 00491 Bit32u ret = 0; 00492 const Bit8u* dat = (Bit8u*)this->cookie; 00493 Bit32u oldChecksum = checksum; 00494 checksum = 0; 00495 for (size_t i = 0; i < sizeof(DynamicHeader); i++) { 00496 ret += dat[i]; 00497 } 00498 checksum = oldChecksum; 00499 return ~ret; 00500 } 00501 00502 bool imageDiskVHD::DynamicHeader::IsValid() { 00503 return ( 00504 memcmp(cookie, "cxsparse", 8) == 0 && 00505 headerVersion >= 0x00010000 && 00506 headerVersion <= 0x0001FFFF && 00507 checksum == CalculateChecksum()); 00508 }