DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/ints/bios_vhd.cpp
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
00016 *  along with this program; if not, write to the Free Software
00017 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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         char* lastchar = indata + dataLength;
00215         int utf32code = 0;
00216 #if defined (WIN32) || defined(OS2)
00217         int iso8859_1code = 0;
00218 #else
00219         char temp[10];
00220         char* tempout;
00221         char* tempout2;
00222         char* tempoutmax = &temp[10];
00223 #endif
00224         while (indata < lastchar) {
00225                 //decode the character
00226                 utf32code = utf16le_decode((const char**)&indata, lastchar);
00227                 if (utf32code < 0) return false;
00228 #if defined (WIN32) || defined(OS2)
00229                 //MSDN docs define fopen to accept strings in the windows default code page, which is typically Windows-1252
00230                 //convert unicode string to ISO-8859, which is a subset of Windows-1252, and a lot easier to implement
00231                 iso8859_1code = iso8859_1_encode(utf32code);
00232                 if (iso8859_1code < 0) return false;
00233                 //and note that backslashes stay as backslashes on windows
00234                 string += (char)iso8859_1code;
00235 #else
00236                 //backslashes become slashes on Linux
00237                 if (utf32code == '\\') utf32code = '/';
00238                 //Linux ext3 filenames are byte strings, typically in UTF-8
00239                 //encode the character to a temporary buffer
00240                 tempout = temp;
00241                 if (utf8_encode(&tempout, tempoutmax, (unsigned int)utf32code) < 0) return false;
00242                 //and append the byte(s) to the string
00243                 tempout2 = temp;
00244                 while (tempout2 < tempout) string += *tempout2++;
00245 #endif
00246         }
00247         return true;
00248 }
00249 
00250 imageDiskVHD::ErrorCodes imageDiskVHD::TryOpenParent(const char* childFileName, const imageDiskVHD::ParentLocatorEntry &entry, Bit8u* data, const Bit32u dataLength, imageDisk** disk, const Bit8u* uniqueId) {
00251         std::string str = "";
00252         const char* slashpos = NULL;
00253 
00254         switch (entry.platformCode) {
00255         case 0x57327275:
00256                 //Unicode relative pathname (UTF-16) on Windows
00257 
00258 #if defined (WIN32) || defined(OS2)
00259                 /* Windows */
00260                 slashpos = strrchr(childFileName, '\\');
00261 #else
00262                 /* Linux */
00263                 slashpos = strrchr(childFileName, '/');
00264 #endif
00265                 if (slashpos != NULL) {
00266                         //copy all characters up to and including the slash, to str
00267                         for (char* pos = (char*)childFileName; pos <= slashpos; pos++) {
00268                                 str += *pos;
00269                         }
00270                 }
00271 
00272                 //convert byte order, and UTF-16 to ASCII, and change backslashes to slashes if on Linux
00273                 if (!convert_UTF16_for_fopen(str, data, dataLength)) break;
00274 
00275                 return imageDiskVHD::Open(str.c_str(), true, disk, uniqueId);
00276 
00277         case 0x57326B75:
00278                 //Unicode absolute pathname (UTF-16) on Windows
00279 
00280 #if defined (WIN32) || defined(OS2)
00281                 /* nothing */
00282 #else
00283                 // Linux
00284                 // Todo: convert absolute pathname to something applicable for Linux
00285                 break;
00286 #endif
00287 
00288                 //convert byte order, and UTF-16 to ASCII, and change backslashes to slashes if on Linux
00289                 if (!convert_UTF16_for_fopen(str, data, dataLength)) break;
00290 
00291                 return imageDiskVHD::Open(str.c_str(), true, disk, uniqueId);
00292 
00293         case 0x4D616320:
00294                 //Mac OS alias stored as blob
00295                 break;
00296         case 0x4D616358:
00297                 //Mac OSX file url (RFC 2396)
00298                 break;
00299         }
00300         return ERROR_OPENING; //return ERROR_OPENING if the file does not exist, cannot be accessed, etc
00301 }
00302 
00303 Bit8u imageDiskVHD::Read_AbsoluteSector(Bit32u sectnum, void * data) {
00304         Bit32u blockNumber = sectnum / sectorsPerBlock;
00305         Bit32u sectorOffset = sectnum % sectorsPerBlock;
00306         if (!loadBlock(blockNumber)) return 0x05; //can't load block
00307         if (currentBlockAllocated) {
00308                 Bit32u byteNum = sectorOffset / 8;
00309                 Bit32u bitNum = sectorOffset % 8;
00310                 bool hasData = currentBlockDirtyMap[byteNum] & (1 << (7 - bitNum));
00311                 if (hasData) {
00312                         if (fseeko64(diskimg, (off_t)(((Bit64u)currentBlockSectorOffset + blockMapSectors + sectorOffset) * 512ull), SEEK_SET)) return 0x05; //can't seek
00313                         if (fread(data, sizeof(Bit8u), 512, diskimg) != 512) return 0x05; //can't read
00314                         return 0;
00315                 }
00316         }
00317         if (parentDisk) {
00318                 return parentDisk->Read_AbsoluteSector(sectnum, data);
00319         }
00320         else {
00321                 memset(data, 0, 512);
00322                 return 0;
00323         }
00324 }
00325 
00326 Bit8u imageDiskVHD::Write_AbsoluteSector(Bit32u sectnum, const void * data) {
00327         Bit32u blockNumber = sectnum / sectorsPerBlock;
00328         Bit32u sectorOffset = sectnum % sectorsPerBlock;
00329         if (!loadBlock(blockNumber)) return 0x05; //can't load block
00330         if (!currentBlockAllocated) {
00331                 if (!copiedFooter) {
00332                         //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)
00333                         if (fseeko64(diskimg, (off_t)0, SEEK_SET)) return 0x05;
00334                         if (fwrite(originalFooter.cookie, sizeof(Bit8u), 512, diskimg) != 512) return 0x05;
00335                         copiedFooter = true;
00336                         //flush the data to disk after writing the backup footer
00337                         if (fflush(diskimg)) return 0x05;
00338                 }
00339                 //calculate new location of footer, and round up to nearest 512 byte increment "just in case"
00340                 Bit64u newFooterPosition = (((footerPosition - 512ull + blockMapSize + dynamicHeader.blockSize) + 511ull) / 512ull) * 512ull;
00341                 //attempt to extend the length appropriately first (on some operating systems this will extend the file)
00342                 if (fseeko64(diskimg, (off_t)newFooterPosition + 512, SEEK_SET)) return 0x05;
00343                 //now write the footer
00344                 if (fseeko64(diskimg, (off_t)newFooterPosition, SEEK_SET)) return 0x05;
00345                 if (fwrite(originalFooter.cookie, sizeof(Bit8u), 512, diskimg) != 512) return 0x05;
00346                 //save the new block location and new footer position
00347                 Bit32u newBlockSectorNumber = (Bit32u)((footerPosition + 511ul) / 512ul);
00348                 footerPosition = newFooterPosition;
00349                 //clear the dirty flags for the new footer position
00350                 for (Bit32u i = 0; i < blockMapSize; i++) currentBlockDirtyMap[i] = 0;
00351                 //write the dirty map
00352                 if (fseeko64(diskimg, (off_t)(newBlockSectorNumber * 512ull), SEEK_SET)) return 0x05;
00353                 if (fwrite(currentBlockDirtyMap, sizeof(Bit8u), blockMapSize, diskimg) != blockMapSize) return 0x05;
00354                 //flush the data to disk after expanding the file, before allocating the block in the BAT
00355                 if (fflush(diskimg)) return 0x05;
00356                 //update the BAT
00357                 if (fseeko64(diskimg, (off_t)(dynamicHeader.tableOffset + (blockNumber * 4ull)), SEEK_SET)) return 0x05;
00358                 Bit32u newBlockSectorNumberBE = SDL_SwapBE32(newBlockSectorNumber);
00359                 if (fwrite(&newBlockSectorNumberBE, sizeof(Bit8u), 4, diskimg) != 4) return false;
00360                 currentBlockAllocated = true;
00361                 currentBlockSectorOffset = newBlockSectorNumber;
00362                 //flush the data to disk after allocating a block
00363                 if (fflush(diskimg)) return 0x05;
00364         }
00365         //current block has now been allocated
00366         Bit32u byteNum = sectorOffset / 8;
00367         Bit32u bitNum = sectorOffset % 8;
00368         bool hasData = currentBlockDirtyMap[byteNum] & (1 << (7 - bitNum));
00369         //if the sector hasn't been marked as dirty, mark it as dirty
00370         if (!hasData) {
00371                 currentBlockDirtyMap[byteNum] |= 1 << (7 - bitNum);
00372                 if (fseeko64(diskimg, (off_t)(currentBlockSectorOffset * 512ull), SEEK_SET)) return 0x05; //can't seek
00373                 if (fwrite(currentBlockDirtyMap, sizeof(Bit8u), blockMapSize, diskimg) != blockMapSize) return 0x05;
00374         }
00375         //current sector has now been marked as dirty
00376         //write the sector
00377         if (fseeko64(diskimg, (off_t)(((Bit64u)currentBlockSectorOffset + (Bit64u)blockMapSectors + (Bit64u)sectorOffset) * 512ull), SEEK_SET)) return 0x05; //can't seek
00378         if (fwrite(data, sizeof(Bit8u), 512, diskimg) != 512) return 0x05; //can't write
00379         return 0;
00380 }
00381 
00382 imageDiskVHD::VHDTypes imageDiskVHD::GetVHDType(const char* fileName) {
00383         imageDisk* disk;
00384         if (Open(fileName, true, &disk)) return VHD_TYPE_NONE;
00385         imageDiskVHD* vhd = dynamic_cast<imageDiskVHD*>(disk);
00386         VHDTypes ret = VHD_TYPE_FIXED; //fixed if an imageDisk was returned
00387         if (vhd) ret = vhd->footer.diskType; //get the actual type if an imageDiskVHD was returned
00388         delete disk;
00389         return ret;
00390 }
00391 
00392 bool imageDiskVHD::loadBlock(const Bit32u blockNumber) {
00393         if (currentBlock == blockNumber) return true;
00394         if (blockNumber >= dynamicHeader.maxTableEntries) return false;
00395         if (fseeko64(diskimg, (off_t)(dynamicHeader.tableOffset + (blockNumber * 4ull)), SEEK_SET)) return false;
00396         Bit32u blockSectorOffset;
00397         if (fread(&blockSectorOffset, sizeof(Bit8u), 4, diskimg) != 4) return false;
00398         blockSectorOffset = SDL_SwapBE32(blockSectorOffset);
00399         if (blockSectorOffset == 0xFFFFFFFFul) {
00400                 currentBlock = blockNumber;
00401                 currentBlockAllocated = false;
00402         }
00403         else {
00404                 if (fseeko64(diskimg, (off_t)(blockSectorOffset * (Bit64u)512), SEEK_SET)) return false;
00405                 currentBlock = 0xFFFFFFFFul;
00406                 currentBlockAllocated = true;
00407                 currentBlockSectorOffset = blockSectorOffset;
00408                 if (fread(currentBlockDirtyMap, sizeof(Bit8u), blockMapSize, diskimg) != blockMapSize) return false;
00409                 currentBlock = blockNumber;
00410         }
00411         return true;
00412 }
00413 
00414 imageDiskVHD::~imageDiskVHD() {
00415         if (currentBlockDirtyMap) {
00416                 free(currentBlockDirtyMap);
00417                 currentBlockDirtyMap = 0;
00418         }
00419         if (parentDisk) {
00420                 parentDisk->Release();
00421                 parentDisk = 0;
00422         }
00423 }
00424 
00425 void imageDiskVHD::VHDFooter::SwapByteOrder() {
00426         features = SDL_SwapBE32(features);
00427         fileFormatVersion = SDL_SwapBE32(fileFormatVersion);
00428         dataOffset = SDL_SwapBE64(dataOffset);
00429         timeStamp = SDL_SwapBE32(timeStamp);
00430         creatorVersion = SDL_SwapBE32(creatorVersion);
00431         creatorHostOS = SDL_SwapBE32(creatorHostOS);
00432         originalSize = SDL_SwapBE64(originalSize);
00433         currentSize = SDL_SwapBE64(currentSize);
00434         geometry.cylinders = SDL_SwapBE16(geometry.cylinders);
00435         diskType = (VHDTypes)SDL_SwapBE32((Bit32u)diskType);
00436         checksum = SDL_SwapBE32(checksum);
00437         //guid might need the byte order swapped also
00438         //however, for our purposes (comparing to the parent guid on differential disks),
00439         //  it doesn't matter so long as we are consistent
00440 }
00441 
00442 Bit32u imageDiskVHD::VHDFooter::CalculateChecksum() {
00443         //checksum is one's complement of sum of bytes excluding the checksum
00444         //because of that, the byte order doesn't matter when calculating the checksum
00445         //however, the checksum must be stored in the correct byte order or it will not match
00446 
00447         Bit32u ret = 0;
00448         Bit8u* dat = (Bit8u*)this->cookie;
00449         Bit32u oldChecksum = checksum;
00450         checksum = 0;
00451         for (size_t i = 0; i < sizeof(VHDFooter); i++) {
00452                 ret += dat[i];
00453         }
00454         checksum = oldChecksum;
00455         return ~ret;
00456 }
00457 
00458 bool imageDiskVHD::VHDFooter::IsValid() {
00459         return (
00460                 memcmp(cookie, "conectix", 8) == 0 &&
00461                 fileFormatVersion >= 0x00010000 &&
00462                 fileFormatVersion <= 0x0001FFFF &&
00463                 checksum == CalculateChecksum());
00464 }
00465 
00466 void imageDiskVHD::DynamicHeader::SwapByteOrder() {
00467         dataOffset = SDL_SwapBE64(dataOffset);
00468         tableOffset = SDL_SwapBE64(tableOffset);
00469         headerVersion = SDL_SwapBE32(headerVersion);
00470         maxTableEntries = SDL_SwapBE32(maxTableEntries);
00471         blockSize = SDL_SwapBE32(blockSize);
00472         checksum = SDL_SwapBE32(checksum);
00473         parentTimeStamp = SDL_SwapBE32(parentTimeStamp);
00474         for (int i = 0; i < 256; i++) {
00475                 parentUnicodeName[i] = SDL_SwapBE16(parentUnicodeName[i]);
00476         }
00477         for (int i = 0; i < 8; i++) {
00478                 parentLocatorEntry[i].platformCode = SDL_SwapBE32(parentLocatorEntry[i].platformCode);
00479                 parentLocatorEntry[i].platformDataSpace = SDL_SwapBE32(parentLocatorEntry[i].platformDataSpace);
00480                 parentLocatorEntry[i].platformDataLength = SDL_SwapBE32(parentLocatorEntry[i].platformDataLength);
00481                 parentLocatorEntry[i].reserved = SDL_SwapBE32(parentLocatorEntry[i].reserved);
00482                 parentLocatorEntry[i].platformDataOffset = SDL_SwapBE64(parentLocatorEntry[i].platformDataOffset);
00483         }
00484         //parent guid might need the byte order swapped also
00485         //however, for our purposes (comparing to the parent guid on differential disks),
00486         //  it doesn't matter so long as we are consistent
00487 }
00488 
00489 Bit32u imageDiskVHD::DynamicHeader::CalculateChecksum() {
00490         //checksum is one's complement of sum of bytes excluding the checksum
00491         //because of that, the byte order doesn't matter when calculating the checksum
00492         //however, the checksum must be stored in the correct byte order or it will not match
00493 
00494         Bit32u ret = 0;
00495         Bit8u* dat = (Bit8u*)this->cookie;
00496         Bit32u oldChecksum = checksum;
00497         checksum = 0;
00498         for (size_t i = 0; i < sizeof(DynamicHeader); i++) {
00499                 ret += dat[i];
00500         }
00501         checksum = oldChecksum;
00502         return ~ret;
00503 }
00504 
00505 bool imageDiskVHD::DynamicHeader::IsValid() {
00506         return (
00507                 memcmp(cookie, "cxsparse", 8) == 0 &&
00508                 headerVersion >= 0x00010000 &&
00509                 headerVersion <= 0x0001FFFF &&
00510                 checksum == CalculateChecksum());
00511 }