123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148(*
* Copyright (C) 2015 David Scott <dave.scott@unikernel.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*)openLwt.InfixopenMirage_block_lwt_smoduleMake_seekable(B:S)=structincludeBletseek_mapped_sector=Lwt.return(Oksector)letseek_unmappedt_=B.get_infot>>=funinfo->Lwt.return(Okinfo.Mirage_block.size_sectors)endmoduleSparse_copy(From:SEEKABLE)(Dest:S)=structmoduleB=Mirage_blocktypeerror=[|`Different_sizes|`Is_read_only|`AofFrom.error|`BofDest.write_error]letpp_errorppf=function|`Different_sizes->Fmt.stringppf"The blocks have different size"|`Is_read_only->Fmt.stringppf"The destination is read-only"|`Ae->From.pp_errorppfe|`Be->Dest.pp_write_errorppfeletv~src:(from:From.t)~dst:(dest:Dest.t)=From.get_infofrom>>=funfrom_info->Dest.get_infodest>>=fundest_info->lettotal_size_from=Int64.(mulfrom_info.B.size_sectors(of_intfrom_info.B.sector_size))inlettotal_size_dest=Int64.(muldest_info.B.size_sectors(of_intdest_info.B.sector_size))iniftotal_size_from<>total_size_destthenLwt.return(Error`Different_sizes)elsebegin(* We'll run multiple threads to try to overlap I/O *)letnext_from_sector=ref0Linletnext_dest_sector=ref0Linletfailure=refNoneinletm=Lwt_mutex.create()inletrecord_failuree=Lwt_mutex.with_lockm(fun()->match!failurewith|Some_->Lwt.return()|None->failure:=Somee;Lwt.return())inletthread()=(* A page-aligned 64KiB buffer *)letbuffer=Io_page.(to_cstruct(get8))inletfrom_sectors=Cstruct.lenbuffer/from_info.B.sector_sizeinletdest_sectors=Cstruct.lenbuffer/dest_info.B.sector_sizeinletrecloop()=(* Grab a region of the disk to copy *)Lwt_mutex.with_lockm(fun()->letnext_from=!next_from_sectorinletnext_dest=!next_dest_sectorinnext_from_sector:=Int64.(addnext_from(of_intfrom_sectors));next_dest_sector:=Int64.(addnext_dest(of_intdest_sectors));Lwt.return(next_from,next_dest))>>=fun(next_from,next_dest)->ifnext_from>=from_info.B.size_sectorsthenLwt.return()elsebegin(* Copy from [next_from, next_from + from_sectors], omitting
unmapped subregions *)letrecinnerxy=ifx>=Int64.(addnext_from(of_intfrom_sectors))||x>=from_info.B.size_sectorsthenloop()elsebeginFrom.seek_mappedfromx>>=function|Errore->record_failure(`Ae)|Okx'->ifx'>xtheninnerx'Int64.(addy(subx'x))elsebeginFrom.seek_unmappedfromx>>=function|Errore->record_failure(`Ae)|Oknext_unmapped->(* Copy up to the unmapped block, or the end of
our chunk... *)letcopy_up_to=minnext_unmappedInt64.(addnext_from(of_intfrom_sectors))inletremaining=Int64.subcopy_up_toxinletthis_time=min(Int64.to_intremaining)from_sectorsinletbuf=Cstruct.subbuffer0(from_info.B.sector_size*this_time)inFrom.readfromx[buf]>>=function|Errore->record_failure(`Ae)|Ok()->Dest.writedesty[buf]>>=function|Errore->record_failure(`Be)|Ok()->innerInt64.(addx(of_intthis_time))Int64.(addy(of_intthis_time))endendininnernext_fromnext_destendinloop()inletthreads=List.mapthread[();();();();();();();()]inLwt.jointhreads>|=fun()->match!failurewith|None->Ok()|Somee->ErroreendendmoduleCopy(From:S)(Dest:S)=structmoduleFrom_seekable=Make_seekable(From)moduleSparse_copy=Sparse_copy(From_seekable)(Dest)typeerror=Sparse_copy.errorletpp_error=Sparse_copy.pp_errorletv~src~dst=Sparse_copy.v~src~dstend