123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181(*
* Copyright (C) 2011-2013 Citrix Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; version 2.1 only. with the special
* exception on linking described in file LICENSE.
*
* 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 Lesser General Public License for more details.
*)openOUnitopenLwtmoduleImpl=Vhd_format.F.From_file(IO)moduleF=Vhd_format.FmoduleField=F.Vhd.FieldopenImplmoduleMemory=structletallocbytes=ifbytes=0thenCstruct.create0elseletn=max1((bytes+4095)/4096)inletpages=Io_page.(to_cstruct(getn))inCstruct.subpages0bytesendletdisk_name_stem="/tmp/dynamic."letdisk_suffix=".vhd"letmake_new_filename=letcounter=ref0infun()->letthis=!counterinincrcounter;disk_name_stem^(string_of_intthis)^disk_suffixlet_fill_sector_withpattern=letb=Memory.alloc512infori=0to511doCstruct.set_charbi(pattern.[imod(String.lengthpattern)])done;blet_absolute_sector_ofvhd_position{Vhd_format.Patterns.block;sector}=letopenFinifvhd.Vhd.header.Header.max_table_entries=0thenNoneelseletblock=matchblockwith|First->0|Last->vhd.Vhd.header.Header.max_table_entries-1inletsectors_per_block=1lslvhd.Vhd.header.Header.block_size_sectors_shiftinletrelative_sector=matchsectorwith|First->0|Last->sectors_per_block-1inSome(Int64.(add(mul(of_intblock)(of_intsectors_per_block))(of_intrelative_sector)))letcstruct_to_stringc=String.escaped(Cstruct.to_stringc)(* Verify that vhd [t] contains the sectors [expected] *)letcheck_written_sectorstexpected=lety=Memory.alloc512inletrecloop=function|[]->return()|(x,data)::xs->Vhd_IO.read_sectortxy>>=funempty->(matchemptywith|false->fail(Failure"read empty sector, expected data")|true->assert_equal~printer:cstruct_to_string~cmp:F.cstruct_equaldatay;return())>>=fun()->loopxsinloopexpectedletempty_sector=Memory.alloc512(* Verify the raw data stream from [t] contains exactly [expected] and no more.
We consider sectors missing from the bitmap as being identical to sectors
present in the bitmap but which are full of zeroes. *)letcheck_raw_stream_contentstexpected=Vhd_input.rawt>>=funstream->fold_left(funoffsetx->matchxwith|`Emptyy->(* all sectors in [offset, offset + y = 1] should not be in the contents list *)List.iter(fun(x,_)->ifx>=offset&&x<(Int64.addoffsety)thenfailwith(Printf.sprintf"Sector %Ld is not supposed to be empty"x))expected;return(Int64.addoffsety)|`Copy(handle,offset',len)->(* all sectors in [offset, offset + len - 1] should be in the contents list *)(* XXX: this won't cope with very large copy requests *)letdata=Memory.alloc(Int64.to_intlen*512)inIO.really_readhandle(Int64.(muloffset'512L))data>>=fun()->letrecchecki=ifi>=(Int64.to_intlen)then()elseletsector=Int64.(addoffset(of_inti))inletactual=Cstruct.subdata(i*512)512inifnot(List.mem_assocsectorexpected)thenbeginassert_equal~printer:cstruct_to_string~cmp:F.cstruct_equalempty_sectoractualendelsebeginletexpected=List.assocsectorexpectedinassert_equal~printer:cstruct_to_string~cmp:F.cstruct_equalexpectedactual;end;check(i+1)incheck0;return(Int64.(addoffsetlen))|`Sectorsdata->letrecloopoffsetremaining=ifCstruct.lenremaining=0thenreturnoffsetelse(* the sector [offset] should be in the contents list *)ifnot(List.mem_assocoffsetexpected)thenfailwith(Printf.sprintf"Sector %Ld is not supposed to be written to"offset)elseletexpected=List.assocoffsetexpectedinletactual=Cstruct.subremaining0F.sector_sizeinassert_equal~printer:cstruct_to_string~cmp:F.cstruct_equalexpectedactual;loop(Int64.(addoffset1L))(Cstruct.shiftremainingF.sector_size)inloopoffsetdata)0Lstream.elements>>=funnext_sector->(* [next_sector] should be higher than the highest sector in the contents list *)lethighest_sector=List.fold_leftmax(-1L)(List.mapfstexpected)inassert(next_sector>highest_sector);return()letverifytcontents=letopenFinletcapacity=Int64.(shift_left(of_intt.Vhd.header.Header.max_table_entries)(t.Vhd.header.Header.block_size_sectors_shift+sector_shift))in(ifcapacity<t.Vhd.footer.Footer.current_sizethenfail(Failure(Printf.sprintf"insufficient capacity in vhd: max table entries = %d; capacity = %Ld; current_size = %Ld"t.Vhd.header.Header.max_table_entriescapacityt.Vhd.footer.Footer.current_size))elsereturn())>>=fun()->check_written_sectorstcontents>>=fun()->check_raw_stream_contentstcontents>>=fun()->letwrite_streamfdstream=fold_left(funoffsetx->matchxwith|`Emptyy->return(Int64.(addoffset(muly512L)))|`Sectorsdata->IO.really_writefdoffsetdata>>=fun()->return(Int64.(addoffset(of_int(Cstruct.lendata))))|`Copy(fd',offset',len')->letbuf=Memory.alloc(Int64.to_intlen'*512)inIO.really_readfd'(Int64.muloffset'512L)buf>>=fun()->IO.really_writefdoffsetbuf>>=fun()->return(Int64.(addoffset(of_int(Cstruct.lenbuf)))))0Lstream.elementsin(* Stream the contents as a fresh vhd *)letfilename=make_new_filename()inIO.createfilename>>=funfd->Vhd_input.vhdt>>=funstream->write_streamfdstream>>=fun_->IO.closefd>>=fun()->(* Check the contents look correct *)Vhd_IO.openchainfilenamefalse>>=funt'->check_written_sectorst'contents>>=fun()->check_raw_stream_contentst'contents>>=fun()->Vhd_IO.closet'>>=fun()->(* Stream the contents as a fresh vhd with a batmap *)letfilename=make_new_filename()inIO.createfilename>>=funfd->Vhd_input.vhd~emit_batmap:truet>>=funstream->write_streamfdstream>>=fun_->IO.closefd>>=fun()->(* Check the contents look correct *)Vhd_IO.openchainfilenamefalse>>=funt'->check_written_sectorst'contents>>=fun()->check_raw_stream_contentst'contents>>=fun()->Vhd_IO.closet'