/************************************************************************* * UrBackup - Client/Server backup system * Copyright (C) 2011-2016 Martin Raiber * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . **************************************************************************/ #include "vhdfile.h" #include "../Interface/Server.h" #include "../Interface/Types.h" #include "../stringtools.h" #include "CompressedFile.h" #include #include #include #include "FileWrapper.h" #include "ClientBitmap.h" #ifdef _WIN32 #include #else #include #endif #include "fs/ntfs.h" const uint64 def_header_offset=0; const uint64 def_dynamic_header_offset=512; const uint64 def_bat_offset=512+1024; const uint64 def_bat_offset_parent=2*1024; const int64 unixtime_offset=946684800; const unsigned int sector_size=512; VHDFile::VHDFile(const std::string &fn, bool pRead_only, uint64 pDstsize, unsigned int pBlocksize, bool fast_mode, bool compress) : dstsize(pDstsize), blocksize(pBlocksize), fast_mode(fast_mode), bitmap_offset(0), bitmap_dirty(false), volume_offset(0), finished(false), file(NULL) { compressed_file=NULL; parent=NULL; read_only=pRead_only; is_open=false; curr_offset=0; currblock=0xFFFFFFFF; backing_file = Server->openFile(fn, (read_only ? MODE_READ : MODE_RW)); bool openedExisting = true; if(!backing_file) { if(read_only==false) { backing_file = Server->openFile(fn, MODE_RW_CREATE); openedExisting=false; } if(backing_file==NULL) { Server->Log("Error opening VHD file", LL_ERROR); return; } } if(check_if_compressed() || compress) { compressed_file = new CompressedFile(backing_file, openedExisting, read_only); file = compressed_file; if(compressed_file->hasError()) { return; } } else { file = backing_file; } if(file->Size()==0 && !read_only) // created file { header_offset=def_header_offset; dynamic_header_offset=def_dynamic_header_offset; bat_offset=def_bat_offset; batsize=(unsigned int)(dstsize/blocksize); if(dstsize%blocksize!=0) ++batsize; bat=new unsigned int[batsize]; for(size_t i=0;iSize()-sector_size; if(nextblock_offset%sector_size!=0) nextblock_offset+=sector_size-nextblock_offset%sector_size; is_open=true; } } } } } } VHDFile::VHDFile(const std::string &fn, const std::string &parent_fn, bool pRead_only, bool fast_mode, bool compress, uint64 pDstsize) : fast_mode(fast_mode), bitmap_offset(0), bitmap_dirty(false), volume_offset(0), finished(false), file(NULL) { compressed_file=NULL; curr_offset=0; is_open=false; read_only=pRead_only; parent=NULL; curr_offset=0; currblock=0xFFFFFFFF; backing_file=Server->openFile(fn, (read_only?MODE_READ:MODE_RW) ); bool openedExisting = true; if(!backing_file) { if(read_only==false) { backing_file=Server->openFile(fn, MODE_RW_CREATE); openedExisting=false; } if(backing_file==NULL) { Server->Log("Error opening VHD file", LL_ERROR); return; } } if(check_if_compressed() || compress) { file = new CompressedFile(backing_file, openedExisting, read_only); } else { file = backing_file; } parent=new VHDFile(parent_fn, true, 0); if(parent->isOpen()==false) { Server->Log("Error opening parent VHD", LL_ERROR); return; } dstsize=parent->getRealSize(); blocksize=parent->getBlocksize(); if (pDstsize>0 && pDstsize != dstsize) { dstsize = pDstsize; } if(file->Size()==0 && !read_only) // created file { header_offset=def_header_offset; dynamic_header_offset=def_dynamic_header_offset; bat_offset=def_bat_offset_parent; batsize=(unsigned int)(dstsize/blocksize); if(dstsize%blocksize!=0) ++batsize; bat=new unsigned int[batsize]; for(size_t i=0;igetUID(), parent->getTimestamp(), parent_fn ); write_bat(); write_footer(); is_open=true; } else { if(read_footer() ) { if(process_footer() ) { if(read_dynamicheader() ) { if(read_bat() ) { nextblock_offset=file->Size()-sector_size; if(nextblock_offset%sector_size!=0) nextblock_offset+=sector_size-nextblock_offset%sector_size; is_open=true; } } } } } } VHDFile::~VHDFile() { if(!finished && file!=NULL) { finish(); } delete file; delete parent; } bool VHDFile::write_header(bool diff) { footer.cookie[0]='c'; footer.cookie[1]='o'; footer.cookie[2]='n'; footer.cookie[3]='e'; footer.cookie[4]='c'; footer.cookie[5]='t'; footer.cookie[6]='i'; footer.cookie[7]='x'; footer.features=big_endian((unsigned int)0x00000002); footer.format_version=big_endian((unsigned int)0x00010000); footer.data_offset=big_endian(dynamic_header_offset); footer.timestamp=big_endian((unsigned int)(Server->getTimeSeconds()-unixtime_offset)); footer.creator_application[0]='v'; footer.creator_application[1]='p'; footer.creator_application[2]='c'; footer.creator_application[3]=' '; footer.creator_version=big_endian((unsigned int)0x00050003); footer.creator_os=big_endian((unsigned int)0x5769326B); footer.original_size=big_endian(dstsize); footer.current_size=big_endian(dstsize); footer.disk_geometry=calculate_chs(); if(diff) footer.disk_type=big_endian((unsigned int)4); else footer.disk_type=big_endian((unsigned int)3); footer.checksum=0; Server->randomFill(footer.uid, 16); footer.saved_state=0; memset(footer.reserved, 0, 427); footer.checksum=calculate_checksum((unsigned char*)&footer, sizeof(VHDFooter) ); if(!file->Seek(header_offset)) return false; _u32 rc=file->Write((char*)&footer, sizeof(VHDFooter)); if(sizeof(VHDFooter)!=rc) return false; else return true; } unsigned int VHDFile::calculate_chs(void) { int64 totalSectors=dstsize/sector_size; unsigned int cylinderTimesHeads; unsigned char heads; unsigned char sectorsPerTrack; if (totalSectors > 65535 * 16 * 255) { totalSectors = 65535 * 16 * 255; } if (totalSectors >= 65535 * 16 * 63) { sectorsPerTrack = 255; heads = 16; cylinderTimesHeads = (unsigned int)(totalSectors / sectorsPerTrack); } else { sectorsPerTrack = 17; cylinderTimesHeads = (unsigned int)(totalSectors / sectorsPerTrack); heads = (cylinderTimesHeads + 1023) / 1024; if (heads < 4) { heads = 4; } if (cylinderTimesHeads >= ((unsigned int)heads * 1024) || heads > 16) { sectorsPerTrack = 31; heads = 16; cylinderTimesHeads = (unsigned int)(totalSectors / sectorsPerTrack); } if (cylinderTimesHeads >= ((unsigned int)heads * 1024)) { sectorsPerTrack = 63; heads = 16; cylinderTimesHeads = (unsigned int)(totalSectors / sectorsPerTrack); } } unsigned short cylinders = cylinderTimesHeads / heads; unsigned int chs=(cylinders<<16)+(heads<<8)+sectorsPerTrack; return big_endian(chs); } unsigned int VHDFile::calculate_checksum(const unsigned char * data, size_t dsize) { unsigned int checksum=0; for(size_t i=0;iConvertToUTF16(unicodename_source)); std::string rel_unicodename; if (dirname.find("Image") != std::string::npos) { rel_unicodename = Server->ConvertToUTF16("..\\" +dirname+"\\"+ ExtractFileName(parentfn)); } else { rel_unicodename = Server->ConvertToUTF16(".\\" + ExtractFileName(parentfn)); } std::string abs_unicodename=Server->ConvertToUTF16(parentfn); unicodename.resize(unicodename.size()+2); unicodename[unicodename.size()-2]=0; unicodename[unicodename.size()-1]=0; memcpy(dynamicheader.parent_unicodename, &unicodename[0], unicodename.size()); unsigned int locator_blocks_abs=static_cast((abs_unicodename.size())/sector_size)+(((abs_unicodename.size())%sector_size!=0)?1:0); dynamicheader.parentlocator[0].platform_code=big_endian((unsigned int)0x57326B75); dynamicheader.parentlocator[0].platform_space=big_endian(locator_blocks_abs*sector_size); dynamicheader.parentlocator[0].platform_length=big_endian((unsigned int)abs_unicodename.size()); uint64 abs_locator_offset=def_bat_offset_parent-512; if(locator_blocks_abs>1) { abs_locator_offset=nextblock_offset; nextblock_offset+=abs_locator_offset*sector_size; } dynamicheader.parentlocator[0].platform_offset=big_endian(abs_locator_offset); if(!file->Seek(abs_locator_offset)) return false; _u32 rc=file->Write(abs_unicodename.c_str(), (_u32)abs_unicodename.size()); if(rc!=abs_unicodename.size()) return false; dynamicheader.parentlocator[1].platform_code=big_endian((unsigned int)0x57327275); unsigned int locator_blocks=(rel_unicodename.size())/sector_size+((rel_unicodename.size())%sector_size!=0)?1:0; if(locator_blocks<128) locator_blocks=128; dynamicheader.parentlocator[1].platform_space=big_endian(locator_blocks*sector_size); dynamicheader.parentlocator[1].platform_length=big_endian((unsigned int)rel_unicodename.size()); dynamicheader.parentlocator[1].platform_offset=big_endian(nextblock_offset); if(!file->Seek(nextblock_offset)) return false; rc=file->Write(rel_unicodename.c_str(), (_u32)rel_unicodename.size()); if(rc!=rel_unicodename.size()) return false; nextblock_offset+=locator_blocks*sector_size; } init_bitmap(); dynamicheader.checksum=calculate_checksum((unsigned char*)&dynamicheader, sizeof(VHDDynamicHeader) ); if(!file->Seek(dynamic_header_offset)) return false; _u32 rc=file->Write((char*)&dynamicheader, sizeof(VHDDynamicHeader) ); if(rc!=sizeof(VHDDynamicHeader)) return false; else return true; } bool VHDFile::write_bat(void) { if(!file->Seek(bat_offset)) return false; _u32 rc=file->Write((char*)bat, batsize*sizeof(unsigned int) ); if(rc!=batsize*sizeof(unsigned int)) return false; else return true; } bool VHDFile::write_footer(void) { if(!file->Seek(nextblock_offset))return false; _u32 rc=file->Write((char*)&footer, sizeof(VHDFooter)); if (rc != sizeof(VHDFooter)) { return false; } else { if (file==backing_file && backing_file->Size() != nextblock_offset + sizeof(VHDFooter)) { backing_file->Resize(nextblock_offset + sizeof(VHDFooter)); } return true; } } bool VHDFile::read_footer(void) { size_t fsize=sizeof(VHDFooter); bool b=file->Seek(file->Size()-sizeof(VHDFooter)); if(!b) { Server->Log("Error seeking -2"); return false; } if(file->Read((char*)&footer, sizeof(VHDFooter))!=sizeof(VHDFooter)) { Server->Log("Cannot read footer", LL_ERROR); return false; } unsigned int checksum=footer.checksum; footer.checksum=0; unsigned int cchecksum=calculate_checksum((unsigned char*)&footer, sizeof(VHDFooter) ); if(checksum!=cchecksum) { Server->Log("Footer checksum wrong. Switching to header", LL_ERROR); file->Seek(0); if(file->Read((char*)&footer, sizeof(VHDFooter))!=sizeof(VHDFooter) ) { Server->Log("Cannot read footer", LL_ERROR); return false; } else { checksum=footer.checksum; footer.checksum=0; unsigned int cchecksum=calculate_checksum((unsigned char*)&footer, sizeof(VHDFooter) ); if(checksum!=cchecksum) { Server->Log("Header and footer checksum wrong", LL_ERROR); return false; } else { footer.checksum=checksum; return true; } } } else { footer.checksum=checksum; return true; } } bool VHDFile::process_footer(void) { if(big_endian(footer.format_version)!=0x00010000) { Server->Log("Unrecognized VHD format version", LL_ERROR); return false; } if(big_endian(footer.disk_type)!=3 && big_endian(footer.disk_type)!=4 ) { Server->Log("Unsupported disk type", LL_ERROR); return false; } dstsize=big_endian(footer.current_size); header_offset=0; dynamic_header_offset=big_endian(footer.data_offset); return true; } bool VHDFile::read_dynamicheader(void) { bool b=file->Seek(dynamic_header_offset); if(!b) { Server->Log("Error seeking -2"); return false; } _u32 rc=file->Read((char*)&dynamicheader, sizeof(VHDDynamicHeader) ); if(rc!=sizeof(VHDDynamicHeader)) { Server->Log("Error reading dynamic header", LL_ERROR); return false; } unsigned int checksum=dynamicheader.checksum; dynamicheader.checksum=0; unsigned int cchecksum=calculate_checksum((unsigned char*)&dynamicheader, sizeof(VHDDynamicHeader) ); if(checksum!=cchecksum) { Server->Log("Dynamicheader checksum wrong", LL_ERROR); return false; } dynamicheader.checksum=checksum; bat_offset=big_endian(dynamicheader.tableoffset); batsize=big_endian(dynamicheader.table_entries); blocksize=big_endian(dynamicheader.blocksize); if(big_endian(footer.disk_type)==4) { //differencing hd std::string parent_unicodename; parent_unicodename.resize(512); memcpy(&parent_unicodename[0], dynamicheader.parent_unicodename, 512); parent_unicodename=big_endian_utf16(parent_unicodename); std::wstring parent_fn=Server->ConvertToWchar(Server->ConvertFromUTF16(parent_unicodename)); parent_fn.resize(wcslen(parent_fn.c_str())); std::string curr_dir = ExtractFilePath(file->getFilename()); bool is_in_other_folder = false; while(parent_fn.size()>3 && (parent_fn.find(L"../")==0 || parent_fn.find(L"..\\")==0 ) ) { curr_dir = ExtractFilePath(curr_dir); parent_fn = parent_fn.substr(3); is_in_other_folder = true; } parent_fn=Server->ConvertToWchar(curr_dir)+L"/"+parent_fn; std::string utf8_parent_fn = Server->ConvertFromWchar(parent_fn); Server->Log("VHD-Parent: \""+utf8_parent_fn+"\"", LL_INFO); if (!FileExists(utf8_parent_fn)) { if (is_in_other_folder) { parent_fn = Server->ConvertToWchar(ExtractFilePath(file->getFilename())) + L"/" + Server->ConvertToWchar(ExtractFileName(Server->ConvertFromWchar(parent_fn))); utf8_parent_fn = Server->ConvertFromWchar(parent_fn); Server->Log("Corrected VHD-Parent to: \"" + utf8_parent_fn + "\"", LL_INFO); } else { parent_fn = Server->ConvertToWchar(ExtractFilePath(ExtractFilePath(file->getFilename()))) + L"/" + Server->ConvertToWchar(ExtractFileName(Server->ConvertFromWchar(parent_fn))); utf8_parent_fn = Server->ConvertFromWchar(parent_fn); Server->Log("Corrected VHD-Parent to: \"" + utf8_parent_fn + "\"", LL_INFO); } } parent=new VHDFile(utf8_parent_fn, true, 0); if(parent->isOpen()==false) { Server->Log("Error opening Parentvhdfile \""+utf8_parent_fn+"\"", LL_ERROR); return false; } if(memcmp(parent->getUID(), dynamicheader.parent_uid, 16)!=0) { Server->Log("Parent uid wrong", LL_ERROR); return false; } if(parent->getTimestamp()!=big_endian(dynamicheader.parent_timestamp) ) { Server->Log("Parent timestamp wrong. Parent was modified? Continuing anyways. But this is dangerous!", LL_ERROR); } } init_bitmap(); return true; } void VHDFile::init_bitmap(void) { bitmap_size=blocksize/sector_size/8; if(blocksize%sector_size!=0 || (blocksize/sector_size)%8!=0) ++bitmap_size; if(bitmap_size%sector_size!=0) bitmap_size+=sector_size-bitmap_size%sector_size; bitmap.resize(bitmap_size); } bool VHDFile::read_bat(void) { bool b=file->Seek(bat_offset); if(!b) { Server->Log("Error seeking -3"); return false; } bat=new unsigned int[batsize]; _u32 rc=file->Read((char*)bat, batsize*sizeof(unsigned int)); if(rc!=batsize*sizeof(unsigned int)) { Server->Log("Error reading BAT", LL_ERROR); return false; } return true; } bool VHDFile::Seek(_i64 offset) { curr_offset=(uint64)offset+volume_offset; return true; } inline bool VHDFile::isBitmapSet(unsigned int offset) { size_t sector=offset/sector_size; size_t bitmap_byte=(size_t)(sector/8); size_t bitmap_bit=sector%8; unsigned char b=bitmap[bitmap_byte]; bool has_bit=((b & (1<<(7-bitmap_bit)))>0); return has_bit; } inline bool VHDFile::setBitmapBit(unsigned int offset, bool v) { size_t sector=offset/sector_size; size_t bitmap_byte=(size_t)(sector/8); size_t bitmap_bit=sector%8; unsigned char b=bitmap[bitmap_byte]; bool has_bit = ((b & (1 << (7 - bitmap_bit)))>0); if(v==true) b=b|(1<<(7-bitmap_bit)); else b=b&(~(1<<(7-bitmap_bit))); bitmap[bitmap_byte]=b; if (has_bit != v) { bitmap_dirty = true; return true; } else { return false; } } bool VHDFile::Read(char* buffer, size_t bsize, size_t &read) { unsigned int block=(unsigned int)(curr_offset/blocksize); size_t blockoffset=curr_offset%blocksize; size_t remaining=blocksize-blockoffset; size_t toread=bsize; bool firstr=true; read=0; if(curr_offset>=dstsize) { return false; } while(true) { unsigned int bat_off=big_endian(bat[block]); if(bat_off==0xFFFFFFFF) { unsigned int wantread=(unsigned int)(std::min)(remaining, toread); if(parent==NULL) memset(&buffer[read], 0, wantread ); else { parent->Seek(curr_offset); size_t p_read; bool b=parent->Read(&buffer[read], wantread, p_read); if(!b) { Server->Log("Reading from parent failed -1", LL_ERROR); } } read+=wantread; curr_offset+=wantread; blockoffset+=wantread; remaining-=wantread; toread-=wantread; if(toread==0) break; else { ++block; blockoffset=0; remaining=blocksize; continue; } } uint64 dataoffset=(uint64)bat_off*(uint64)sector_size; if(block!=currblock) { switchBitmap(dataoffset); file->Seek(dataoffset); if(dataoffset+bitmap_size+blockoffset+bsize>(uint64)file->Size() ) { Server->Log("Wrong dataoffset: "+convert(dataoffset), LL_ERROR); return false; } if(file->Read(reinterpret_cast(bitmap.data()), bitmap_size)!=bitmap_size) { Server->Log("Error reading bitmap", LL_ERROR); return false; } currblock=block; } bool b=file->Seek(dataoffset+bitmap_size+blockoffset); if(!b) Server->Log("Seeking failed!- 1", LL_ERROR); while( blockoffsetsector_size-blockoffset%sector_size ) { wantread=sector_size-blockoffset%sector_size; firstr=false; } else { firstr=false; } if( curr_offset+wantread>dstsize ) { return true; } if( isBitmapSet((unsigned int)blockoffset) ) { _u32 curr_tread = (_u32)wantread; bool has_read_error = false; wantread=(size_t)file->Read(&buffer[read], curr_tread, &has_read_error); if (curr_tread != wantread && has_read_error) { Server->Log("Error reading from VHD file at position " + convert(dataoffset + bitmap_size + blockoffset) + "."); print_last_error(); return false; } } else { if(parent!=NULL) { parent->Seek(curr_offset); bool b=parent->Read(&buffer[read], wantread, wantread); if(!b) { Server->Log("Reading from parent failed -2", LL_ERROR); } } else { memset(&buffer[read], 0, wantread ); } file->Seek(dataoffset+bitmap_size+blockoffset+wantread); } read+=wantread; curr_offset+=wantread; blockoffset+=wantread; remaining-=wantread; toread-=wantread; if(toread==0) { break; } if(remaining==0) { break; } } if(toread==0) break; ++block; blockoffset=0; remaining=blocksize; if(curr_offset>=dstsize) { return true; } } return true; } _u32 VHDFile::Write(const char *buffer, _u32 bsize, bool *has_error) { if(read_only) { Server->Log("VHD file is read only", LL_ERROR); if(has_error) *has_error=true; return 0; } if(bsize+curr_offset>dstsize) { Server->Log("VHD file is not large enough. Want to write till "+convert(bsize+curr_offset)+" but size is "+convert(dstsize), LL_ERROR); if(has_error) *has_error=true; return 0; } bool dwrite_footer=false; uint64 block=curr_offset/((uint64)blocksize); size_t blockoffset=curr_offset%blocksize; size_t remaining=blocksize-blockoffset; size_t towrite=bsize; size_t bufferoffset=0; bool firstw=true; while(true) { uint64 dataoffset; unsigned int bat_ref=big_endian(bat[block]); bool new_block=false; if(bat_ref==0xFFFFFFFF) { dataoffset=nextblock_offset; nextblock_offset+=blocksize+bitmap_size; nextblock_offset=nextblock_offset+(sector_size-nextblock_offset%sector_size); dwrite_footer=true; new_block=true; int64 bat_offset = dataoffset / (uint64)(sector_size); if (bat_offset >= UINT_MAX) { Server->Log("Too much data in VHD file. BAT table overflow. Next BAT entry would be to offset " + convert(bat_offset), LL_ERROR); if (has_error) *has_error = true; return 0; } bat[block]=big_endian((unsigned int)(bat_offset)); } else { dataoffset=(uint64)bat_ref*(uint64)sector_size; } if(currblock!=block) { switchBitmap(dataoffset); bool b=file->Seek(dataoffset); if(!b) Server->Log("Seeking failed", LL_ERROR); if(!new_block) file->Read(reinterpret_cast(bitmap.data()), bitmap_size); else { memset(bitmap.data(), 0, bitmap_size ); _u32 rc=file->Write(reinterpret_cast(bitmap.data()), bitmap_size); if(rc!=bitmap_size) { Server->Log("Writing bitmap failed", LL_ERROR); print_last_error(); if(has_error) *has_error=true; return 0; } } currblock=block; } bool b=file->Seek(dataoffset+bitmap_size+blockoffset); if(!b) { Server->Log("Seeking in file failed", LL_ERROR); if(has_error) *has_error=true; return 0; } while( blockoffsetsector_size-blockoffset%sector_size ) { wantwrite=sector_size-blockoffset%sector_size; firstw=false; } else { firstw=false; } setBitmapBit((unsigned int)blockoffset, true); _u32 rc=file->Write(&buffer[bufferoffset], (_u32)wantwrite); if(rc!=wantwrite) { Server->Log("Writing to file failed", LL_ERROR); if(has_error) *has_error=true; print_last_error(); return 0; } bufferoffset+=wantwrite; blockoffset+=wantwrite; remaining-=wantwrite; towrite-=wantwrite; if(towrite==0) { break; } if(remaining==0) { break; } } if(!fast_mode) { file->Seek(dataoffset); _u32 rc=file->Write(reinterpret_cast(bitmap.data()), bitmap_size); if(rc!=bitmap_size) { Server->Log("Writing bitmap failed", LL_ERROR); print_last_error(); if(has_error) *has_error=true; return 0; } } if(towrite==0) break; ++block; blockoffset=0; remaining=blocksize; } if(dwrite_footer && !fast_mode) { if(!write_footer()) { Server->Log("Error writing footer", LL_ERROR); if(has_error) *has_error=true; return 0; } if(!write_bat()) { Server->Log("Error writing BAT", LL_ERROR); if(has_error) *has_error=true; return 0; } } curr_offset+=bsize; return bsize; } _u32 VHDFile::Write(int64 spos, const char *buffer, _u32 bsize, bool* has_error) { if(!Seek(spos)) { if (has_error) *has_error = true; return 0; } return Write(buffer, bsize, has_error); } bool VHDFile::has_block(bool use_parent) { unsigned int block=(unsigned int)(curr_offset/blocksize); size_t blockoffset=curr_offset%blocksize; if(curr_offset>=dstsize) { return false; } unsigned int bat_off=big_endian(bat[block]); if(bat_off==0xFFFFFFFF) { if(parent==NULL || !use_parent) { return false; } else { parent->Seek(curr_offset); return parent->has_block(); } } uint64 dataoffset=(uint64)bat_off*(uint64)sector_size; if(block!=currblock) { switchBitmap(dataoffset); file->Seek(dataoffset); if(dataoffset+bitmap_size+blockoffset>(uint64)file->Size() ) { Server->Log("Wrong dataoffset: "+convert(dataoffset), LL_ERROR); return false; } if(file->Read(reinterpret_cast(bitmap.data()), bitmap_size)!=bitmap_size) { Server->Log("Error reading bitmap", LL_ERROR); return false; } currblock=block; } if( isBitmapSet((unsigned int)blockoffset) ) { return true; } else { if(parent!=NULL && use_parent) { parent->Seek(curr_offset); return parent->has_block(); } else { return false; } } } void VHDFile::switchBitmap(uint64 new_offset) { if(fast_mode && !read_only && bitmap_dirty && bitmap_offset!=0) { file->Seek(bitmap_offset); _u32 rc=file->Write(reinterpret_cast(bitmap.data()), bitmap_size); if(rc!=bitmap_size) { Server->Log("Writing bitmap failed", LL_ERROR); print_last_error(); } bitmap_dirty=false; } bitmap_offset=new_offset; bitmap_dirty=false; } uint64 VHDFile::getSize(void) { return dstsize-volume_offset; } uint64 VHDFile::getRealSize(void) { return dstsize; } uint64 VHDFile::usedSize(void) { uint64 offset_backup=curr_offset; uint64 used_size=0; for(uint64 i=0;iSeek(curr_offset); return parent->has_sector(); } else return false; } else { return true; } } bool VHDFile::this_has_sector(_i64 sector_size) { unsigned int block=(unsigned int)(curr_offset/blocksize); unsigned int bat_ref=big_endian(bat[block]); if(bat_ref==0xFFFFFFFF) { return false; } else { return true; } } char *VHDFile::getUID(void) { return footer.uid; } unsigned int VHDFile::getTimestamp(void) { return big_endian(footer.timestamp); } unsigned int VHDFile::getBlocksize() { return blocksize; } bool VHDFile::isOpen(void) { return is_open; } std::string VHDFile::getFilename(void) { return file->getFilename(); } std::string VHDFile::Read(_u32 tr, bool *has_error) { std::string ret; ret.resize(4096); size_t read; bool b=Read((char*)ret.c_str(), 4096, read); if(!b) { if(has_error) *has_error=true; ret.clear(); return ret; } ret.resize(read); return ret; } std::string VHDFile::Read(int64 spos, _u32 tr, bool* has_error) { if(!Seek(spos)) { if (has_error) *has_error = true; return std::string(); } return Read(tr); } _u32 VHDFile::Read(char* buffer, _u32 bsize, bool *has_error) { size_t read; bool b=Read(buffer, bsize, read); if(!b) { if(has_error) *has_error=true; return 0; } else { return (_u32)read; } } _u32 VHDFile::Read(int64 spos, char* buffer, _u32 bsize, bool* has_error) { if(!Seek(spos)) { if (has_error) *has_error = true; return 0; } return Read(buffer, bsize); } _u32 VHDFile::Write(const std::string &tw, bool *has_error) { return Write(tw.c_str(), (_u32)tw.size(), has_error); } _u32 VHDFile::Write(int64 spos, const std::string &tw, bool *has_error) { return Write(spos, tw.c_str(), (_u32)tw.size(), has_error); } _i64 VHDFile::Size(void) { return (_i64)getSize(); } _i64 VHDFile::RealSize(void) { return (_i64)usedSize(); } void VHDFile::addVolumeOffset(_i64 offset) { volume_offset=offset; } void VHDFile::print_last_error() { #ifdef _WIN32 Server->Log("Last error: "+convert((int)GetLastError()), LL_ERROR); #else Server->Log("Last error: "+convert(errno), LL_ERROR); #endif } bool VHDFile::check_if_compressed() { const char header_magic[] = "URBACKUP COMPRESSED FILE"; std::string magic = backing_file->Read(sizeof(header_magic)-1); return magic == std::string(header_magic); } bool VHDFile::finish() { if (finished) { return true; } if (!is_open) { return false; } switchBitmap(0); if(fast_mode && !read_only) { if(!write_footer()) { Server->Log("Error writing footer", LL_ERROR); return false; } if(!write_bat()) { Server->Log("Error writing BAT", LL_ERROR); return false; } } if(parent!=NULL) { if(!parent->finish()) { return false; } } CompressedFile* compfile = dynamic_cast(file); if(compfile!=NULL) { if (compfile->finish()) { finished = true; return true; } } else { if (read_only) { finished = true; return true; } if (file->Sync()) { finished = true; return true; } } return false; } VHDFile* VHDFile::getParent() { return parent; } bool VHDFile::isCompressed() { return compressed_file!=NULL; } bool VHDFile::makeFull( _i64 fs_offset, IVHDWriteCallback* write_callback) { FileWrapper devfile(this, fs_offset); std::auto_ptr bitmap_source; bitmap_source.reset(new ClientBitmap(backing_file->getFilename() + ".cbitmap")); if (bitmap_source->hasError()) { Server->Log("Error reading client bitmap. Falling back to reading bitmap from NTFS", LL_WARNING); bitmap_source.reset(new FSNTFS(&devfile, IFSImageFactory::EReadaheadMode_None, false, NULL)); } if(bitmap_source->hasError()) { Server->Log("Error opening NTFS bitmap. Cannot convert incremental to full image.", LL_WARNING); return false; } unsigned int bitmap_blocksize = static_cast(bitmap_source->getBlocksize()); std::vector buffer; buffer.resize(sector_size); int64 ntfs_blocks_per_vhd_sector = blocksize / bitmap_blocksize; for(int64 ntfs_block=0, n_ntfs_blocks = devfile.Size()/ bitmap_blocksize; ntfs_blockhasBlock(i) ) { has_vhd_sector = true; break; } } if(has_vhd_sector) { int64 block_pos = fs_offset + ntfs_block*bitmap_blocksize; int64 max_block_pos = (std::min)(fs_offset + ntfs_block*bitmap_blocksize + blocksize, fs_offset + n_ntfs_blocks*bitmap_blocksize); for(int64 i = block_pos;iLog("Error converting incremental to full image. Cannot read from parent VHD file at position "+convert(i), LL_WARNING); return false; } if(!write_callback->writeVHD(i, buffer.data(), sector_size)) { Server->Log("Error converting incremental to full image. Cannot write to VHD file at position "+convert(i), LL_WARNING); return false; } } } } else { int64 block_pos = ntfs_block*bitmap_blocksize; int64 max_block_pos = (std::min)(ntfs_block*bitmap_blocksize + blocksize, n_ntfs_blocks*bitmap_blocksize); write_callback->emptyVHDBlock(block_pos, max_block_pos); } } delete parent; parent = NULL; Server->Log("Writing new headers...", LL_INFO); /** * For some reason Windows won't open the VHD if * the BAT is not shifted to follow the dynamic header */ bat_offset = def_bat_offset; if(!write_header(false) || !write_dynamicheader(NULL, 0, "") || !write_footer() || !write_bat() ) { Server->Log("Error writing new headers", LL_WARNING); return false; } return true; } bool VHDFile::setUnused(_i64 unused_start, _i64 unused_end) { if (!Seek(unused_start)) { Server->Log("Error while sseking to "+convert(unused_end)+" in VHD file. Size is "+convert(dstsize)+" -2", LL_ERROR); return false; } if (read_only) { Server->Log("VHD file is read only -2", LL_ERROR); return false; } if (static_cast(unused_end)>dstsize) { Server->Log("VHD file is not large enough. Want to trim till " + convert(unused_end) + " but size is " + convert(dstsize), LL_ERROR); return false; } bool dwrite_footer = false; uint64 block = curr_offset / ((uint64)blocksize); size_t blockoffset = curr_offset%blocksize; size_t remaining = blocksize - blockoffset; size_t towrite = unused_end- unused_start; size_t bufferoffset = 0; bool firstw = true; std::vector zero_buf; while (true) { uint64 dataoffset; unsigned int bat_ref = big_endian(bat[block]); bool new_block = false; if (bat_ref == 0xFFFFFFFF) { dataoffset = nextblock_offset; nextblock_offset += blocksize + bitmap_size; nextblock_offset = nextblock_offset + (sector_size - nextblock_offset%sector_size); dwrite_footer = true; new_block = true; bat[block] = big_endian((unsigned int)(dataoffset / (uint64)(sector_size))); } else { dataoffset = (uint64)bat_ref*(uint64)sector_size; } if (currblock != block) { switchBitmap(dataoffset); bool b = file->Seek(dataoffset); if (!b) Server->Log("Seeking failed", LL_ERROR); if (!new_block) file->Read(reinterpret_cast(bitmap.data()), bitmap_size); else { memset(bitmap.data(), 0, bitmap_size); _u32 rc = file->Write(reinterpret_cast(bitmap.data()), bitmap_size); if (rc != bitmap_size) { Server->Log("Writing bitmap failed", LL_ERROR); print_last_error(); return false; } } currblock = block; } while (blockoffset < blocksize) { size_t wantwrite = (std::min)((size_t)sector_size, towrite); if (remaining < wantwrite) wantwrite = remaining; if (firstw && blockoffset%sector_size != 0 && wantwrite > sector_size - blockoffset%sector_size) { wantwrite = sector_size - blockoffset%sector_size; firstw = false; } else { firstw = false; } /** * This is counter-intuitive. We want it to return zeroes on reads but if we * set the bit to false it will read from the parent. So set it to true * to read the zeroes from the current VHD file. * This only works if we have not written to the same location previously, so * write zeroes in this case. */ if (!setBitmapBit((unsigned int)blockoffset, true)) { if (zero_buf.size() != wantwrite) { zero_buf.resize(wantwrite); } _u32 rc = file->Write(dataoffset + bitmap_size + blockoffset, zero_buf.data(), (_u32)wantwrite); if (rc != wantwrite) { Server->Log("Writing to file failed (2)", LL_ERROR); print_last_error(); return false; } } bufferoffset += wantwrite; blockoffset += wantwrite; remaining -= wantwrite; towrite -= wantwrite; if (towrite == 0) { break; } if (remaining == 0) { break; } } if (!fast_mode) { file->Seek(dataoffset); _u32 rc = file->Write(reinterpret_cast(bitmap.data()), bitmap_size); if (rc != bitmap_size) { Server->Log("Writing bitmap failed", LL_ERROR); print_last_error(); return false; } } if (towrite == 0) break; ++block; blockoffset = 0; remaining = blocksize; } if (dwrite_footer && !fast_mode) { if (!write_footer()) { Server->Log("Error writing footer (2)", LL_ERROR); return false; } if (!write_bat()) { Server->Log("Error writing BAT (2)", LL_ERROR); return false; } } return true; } bool VHDFile::PunchHole( _i64 spos, _i64 size ) { return false; } bool VHDFile::Sync() { return finish(); } bool VHDFile::setBackingFileSize(_i64 fsize) { if (file != backing_file) { return false; } _i64 start_offset = bat_offset + batsize * sizeof(unsigned int); start_offset = start_offset + (sector_size - start_offset%sector_size); _i64 one_blocksize = blocksize + bitmap_size; one_blocksize = one_blocksize + (sector_size - one_blocksize%sector_size); _i64 nblocks = fsize / blocksize + (fsize%blocksize != 0 ? 1 : 0); _i64 actual_fsize = start_offset + nblocks*one_blocksize; if (actual_fsize > backing_file->Size()) { return backing_file->Resize(actual_fsize, false); } else { return false; } }