123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141(*
* Copyright (C) 2006-2009 Citrix Systems Inc.
* Copyright (C) 2012 Thomas Gazagnaire <thomas@ocamlpro.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.
*)[@@@warning"-3"](* FIXME Tar.HeaderWriter needs to be used here *)moduleDriver=structtypein_channel=Unix.file_descrtypeout_channel=Unix.file_descrletrecwith_restartopfdbufofflen=tryopfdbufofflenwithUnix.Unix_error(Unix.EINTR,_,_)->with_restartopfdbufofflenletrecreally_inputfdbufoff=function|0->()|len->letm=Unix.readfdbufoffleninifm=0thenraiseEnd_of_file;really_inputfdbuf(off+m)(len-m)letrecreally_outputfdbufoff=function|0->()|len->letm=Unix.writefdbufoffleninreally_outputfdbuf(off+m)(len-m)letoutput=with_restartreally_outputletinput=with_restartUnix.readletreally_input=with_restartreally_inputletclose_out=Unix.closeendmoduleT=Tar.Make(Driver)letreally_write=T.really_writeletreally_read=T.really_readletget_next_header=T.get_next_header(** Return the header needed for a particular file on disk *)letheader_of_file?level(file:string):Tar.Header.t=letlevel=matchlevelwithNone->Tar.Header.V7|Somelevel->levelinletstat=Unix.LargeFile.lstatfileinletfile_mode=stat.Unix.LargeFile.st_perminletuser_id=stat.Unix.LargeFile.st_uidinletgroup_id=stat.Unix.LargeFile.st_gidinletmod_time=Int64.of_floatstat.Unix.LargeFile.st_mtimeinletlink_indicator=Tar.Header.Link.Normalinletlink_name=""inletuname=iflevel=V7then""else(Unix.getpwuidstat.Unix.LargeFile.st_uid).Unix.pw_nameinletdevmajor=iflevel=Ustarthenstat.Unix.LargeFile.st_develse0inletgname=iflevel=V7then""else(Unix.getgrgidstat.Unix.LargeFile.st_gid).Unix.gr_nameinletdevminor=iflevel=Ustarthenstat.Unix.LargeFile.st_rdevelse0inTar.Header.make~file_mode~user_id~group_id~mod_time~link_indicator~link_name~uname~gname~devmajor~devminorfilestat.Unix.LargeFile.st_sizeletwrite_block=T.write_blockletwrite_end=T.write_end(** Utility functions for operating over whole tar archives *)moduleArchive=structincludeT.Archiveletwith_filenameflagspermsf=letfd=Unix.openfilenameflagspermsinFun.protect~finally:(fun()->Unix.closefd)(fun()->ffd)(** Extract the contents of a tar to directory 'dest' *)letextractdestifd=letdesthdr=letfilename=desthdr.Tar.Header.file_nameinUnix.openfilefilename[O_WRONLY;O_CLOEXEC]0inextract_gendestifdlettransform?levelfifdofd=letrecloop()=matchget_next_headerifdwith|exceptionTar.Header.End_of_stream->()|header'->letheader=fheader'inletbody=fun_->copy_nifdofdheader.Tar.Header.file_sizeinwrite_block?levelheaderbodyofd;skipifd(Tar.Header.compute_zero_padding_lengthheader');loop()inloop();write_endofd(** Create a tar on file descriptor fd from the filename list
'files' *)letcreatefilesofd=letfiles=letffilename=letstat=Unix.statfilenameinifstat.Unix.st_kind<>Unix.S_REGthen(* Skipping, not a regular file. *)Noneelselethdr=header_of_filefilenameinSome(hdr,(funofd->with_filefilename[O_RDONLY;O_CLOEXEC]0@@funifd->copy_nifdofdhdr.Tar.Header.file_size))inList.filter_mapffilesincreate_gen(Stream.of_listfiles)ofd(** Multicast 'n' bytes from input fd 'ifd' to output fds 'ofds'. NB if one deadlocks
they all stop.*)letmulticast_n?(buffer_size=1024*1024)(ifd:Unix.file_descr)(ofds:Unix.file_descrlist)(n:int64)=letbuffer=Bytes.makebuffer_size'\000'inletrecloop(n:int64)=ifn<=0Lthen()elseletamount=Int64.to_int(minn(Int64.of_int(Bytes.lengthbuffer)))inletread=Unix.readifdbuffer0amountinifread=0thenraiseEnd_of_file;List.iter(funofd->ignore(Unix.writeofdbuffer0read))ofds;loop(Int64.subn(Int64.of_intread))inloopnend