123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400(*
* Copyright (c) 2022-2022 Tarides <contact@tarides.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.
*)openImportincludeControl_file_intfmoduleVersion=Irmin_pack.VersionmoduleChecksum=structletcalculate~encode_bin~set_checksum~payload=letopenCheckseuminletresult=refAdler32.defaultinencode_bin(set_checksumpayloadInt63.zero)(funstr->result:=Adler32.digest_stringstr0(String.lengthstr)!result);Int63.of_int(Optint.to_int!result)letcalculate_and_set~encode_bin~set_checksum~payload=calculate~encode_bin~set_checksum~payload|>set_checksumpayloadletis_valid~encode_bin~set_checksum~get_checksum~payload=Int63.equal(calculate~encode_bin~set_checksum~payload)(get_checksumpayload)endmoduleSerde=structmoduletypeS=sigtypepayloadtyperaw_payloadvalof_bin_string:string->string->(payload,[>`Corrupted_control_fileofstring|`Unknown_major_pack_versionofstring])resultvalraw_of_bin_string:string->string->(raw_payload,[>`Corrupted_control_fileofstring|`Unknown_major_pack_versionofstring])resultvalto_bin_string:payload->stringendletextract_version_and_payloadctxs=letopenResult_syntaxinletlen=String.lengthsinlet*left,right=tryOk(String.subs08,String.subs8(len-8))withInvalid_argument_->Error(`Corrupted_control_filectx)inlet+version=matchVersion.of_binleftwith|None->Error(`Unknown_major_pack_versionleft)|Some(`V1|`V2)->assertfalse(* TODO: create specific error *)|Some((`V3|`V4|`V5)asx)->iflen>Io.Unix.page_sizethenError(`Corrupted_control_filectx)elseOkxin(version,right)moduleUpper:Swithtypepayload=Payload.Upper.Latest.tandtyperaw_payload=Payload.Upper.raw_payload=structmoduleData=structmodulePlv3=structincludePayload.Upper.V3letof_bin_string=Irmin.Type.(unstage(of_bin_stringt))endmodulePlv4=structincludePayload.Upper.V4let is_checksum_validpayload=letencode_bin=Irmin.Type.(unstage(pre_hash t))inletset_checksumpayloadchecksum={payloadwithchecksum}inletget_checksumpayload=payload.checksuminChecksum.is_valid~payload~encode_bin~set_checksum~get_checksumletof_bin_string=Irmin.Type.(unstage(of_bin_stringt))endmodulePlv5=structincludePayload.Upper.V5letchecksum_encode_bin=Irmin.Type.(unstage(pre_hasht))letset_checksumpayloadchecksum={payloadwithchecksum}letget_checksumpayload=payload.checksumletis_checksum_validpayload=Checksum.is_valid~payload~encode_bin:checksum_encode_bin~set_checksum~get_checksumletset_checksumpayload=Checksum.calculate_and_set~encode_bin:checksum_encode_bin~set_checksum~payloadletof_bin_string=Irmin.Type.(unstage(of_bin_stringt))letto_bin_string=Irmin.Type.(unstage(to_bin_stringt))endtypet=Payload.Upper.raw_payload=|Validofversion|Invalidofversionandversion=Payload.Upper.version=|V3ofPlv3.t|V4ofPlv4.t|V5ofPlv5.tletto_bin_string=function|Invalid_|Valid(V3_)|Valid(V4_)->assertfalse|Valid(V5payload)->letpayload=Plv5.set_checksumpayloadinVersion.to_bin`V5^Plv5.to_bin_stringpayloadletof_bin_stringctxs=letopenResult_syntaxinlet*version,payload=extract_version_and_payloadctxsinletroute_version()=matchversionwith|`V3->Plv3.of_bin_stringpayload>>=funpayload->Valid(V3payload)|>Result.ok|`V4->Plv4.of_bin_stringpayload>>=funpayload->(matchPlv4.is_checksum_validpayloadwith|false->Invalid(V4payload)|true->Valid(V4payload))|>Result.ok|`V5->Plv5.of_bin_stringpayload>>=funpayload->(matchPlv5.is_checksum_validpayloadwith|false->Invalid(V5payload)|true->Valid(V5payload))|>Result.okinmatchroute_version()with|Ok_asx->x|Error_->Error(`Corrupted_control_filectx)endmoduleLatest=Data.Plv5typepayload=Latest.ttyperaw_payload=Data.tletupgrade_from_v3(pl:Payload.Upper.V3.t):payload=letchunk_start_idx=ref0inletstatus=matchpl.statuswith|From_v1_v2_post_upgradex->Latest.From_v1_v2_post_upgradex|From_v3_no_gc_yet->No_gc_yet|From_v3_used_non_minimal_indexing_strategy->Used_non_minimal_indexing_strategy|From_v3_gcedx->chunk_start_idx:=x.generation;Gced{suffix_start_offset=x.suffix_start_offset;generation=x.generation;latest_gc_target_offset=x.suffix_start_offset;suffix_dead_bytes=Int63.zero;mapping_end_poff =None;}|T1|T2|T3|T4|T5|T6|T7|T8|T9|T10|T11|T12|T13|T14|T15->(* Unreachable *)assertfalsein{dict_end_poff=pl.dict_end_poff;(* When upgrading from v3 to v4, there is only one (appendable) chunk,
which is the existing suffix, so we set the new [appendable_chunk_poff]
to [pl.suffix_end_poff]. *)appendable_chunk_poff=pl.suffix_end_poff;status;upgraded_from=Some(Version.to_int`V3);checksum=Int63.zero;chunk_start_idx=!chunk_start_idx;chunk_num=1;volume_num=0;}letupgrade_status_from_v4=function|Payload.Upper.V4.From_v1_v2_post_upgradex->Latest.From_v1_v2_post_upgradex|No_gc_yet->No_gc_yet|Used_non_minimal_indexing_strategy->Used_non_minimal_indexing_strategy|Gcedx->Gced{suffix_start_offset=x.suffix_start_offset;generation=x.generation;latest_gc_target_offset=x.latest_gc_target_offset;suffix_dead_bytes=x.suffix_dead_bytes;mapping_end_poff=None;}|T1|T2|T3|T4|T5|T6|T7|T8|T9|T10|T11|T12|T13|T14|T15->(* Unreachable *)assertfalseletupgrade_from_v4(pl:Payload.Upper.V4.t):payload={dict_end_poff=pl.dict_end_poff;appendable_chunk_poff=pl.appendable_chunk_poff;checksum =Int63.zero;chunk_start_idx=pl.chunk_start_idx;chunk_num=pl.chunk_num;status=upgrade_status_from_v4pl.status;upgraded_from=Some(Version.to_int`V4);volume_num=0;}letof_bin_stringctxstring=letopenResult_syntaxinlet*payload=Data.of_bin_stringctxstringinmatchpayloadwith|Invalid_->Error(`Corrupted_control_filectx)|Valid(V3payload)->Ok(upgrade_from_v3payload)|Valid(V4payload)->Ok(upgrade_from_v4payload)|Valid(V5payload)->Okpayload(* Similar yo [of_bin_string] but skips version upgrade *)letraw_of_bin_string=Data.of_bin_stringletto_bin_stringpayload=Data.(to_bin_string(Valid(V5payload)))endmoduleVolume:Swithtypepayload=Payload.Volume.Latest.tandtyperaw_payload=Payload.Volume.raw_payload=structmoduleData=structmodulePlv5=structincludePayload.Volume.V5letchecksum_encode_bin=Irmin.Type.(unstage(pre_hasht))letset_checksumpayloadchecksum={payloadwithchecksum}letget_checksumpayload=payload.checksumletis_checksum_validpayload=Checksum.is_valid~payload~encode_bin:checksum_encode_bin~set_checksum~get_checksumletset_checksumpayload=Checksum.calculate_and_set~encode_bin:checksum_encode_bin~set_checksum~payloadletof_bin_string=Irmin.Type.(unstage(of_bin_stringt))letto_bin_string=Irmin.Type.(unstage(to_bin_stringt))endtypet=Payload.Volume.raw_payload=|Validofversion|Invalidofversionandversion=Payload.Volume.version=V5ofPlv5.tletto_bin_string=function|Invalid_->assertfalse|Valid(V5payload)->letpayload=Plv5.set_checksumpayloadinVersion.to_bin`V5^Plv5.to_bin_stringpayloadletof_bin_stringctxs=letopenResult_syntaxinlet*version,payload=extract_version_and_payloadctxsinletroute_version()=matchversionwith|`V3|`V4->assertfalse|`V5->Plv5.of_bin_stringpayload>>=funpayload->(matchPlv5.is_checksum_validpayloadwith|false->Invalid(V5payload)|true->Valid(V5payload))|>Result.okinmatchroute_version()with|Ok_asx->x|Error_->Error(`Corrupted_control_filectx)endmodulePayload=Data.Plv5typepayload=Payload.ttyperaw_payload=Data.tletof_bin_stringctxstring=letopenResult_syntaxinlet*payload=Data.of_bin_stringctxstringinmatchpayloadwith|Invalid_->Error(`Corrupted_control_filectx)|Valid(V5payload)->Okpayloadletraw_of_bin_string=Data.of_bin_stringletto_bin_stringpayload=Data.(to_bin_string(Valid(V5payload)))endendmoduleMake(Serde:Serde.S)(Io:Io.S)=structmoduleIo=Iotypepayload=Serde.payloadtypet={mutableio:Io.t;mutablepayload:payload;path:string;tmp_path:stringoption;}letwriteiopayload=lets=Serde.to_bin_stringpayloadinIo.write_stringio~off:Int63.zerosletset_payloadtpayload=letopenResult_syntaxinifIo.readonlyt.iothenError`Ro_not_allowedelsematcht.tmp_pathwith|None->Error`No_tmp_path_provided|Sometmp_path->let*()=Io.closet.ioinlet*io_tmp=Io.create~path:tmp_path~overwrite:trueint.io<-io_tmp;let*()=writeio_tmppayloadinlet+()=Io.move_file~src:tmp_path~dst:t.pathint.payload<-payloadletreadio=letopenResult_syntaxinlet*string=Io.read_all_to_stringioinSerde.of_bin_string(Io.pathio)stringletcreate_rw~path~tmp_path~overwrite(payload:payload)=letopenResult_syntaxinlet*io=Io.create~path~overwriteinlet+()=writeiopayloadin{io;payload;path;tmp_path}letopen_~path~tmp_path~readonly=letopenResult_syntaxinlet*io=Io.open_~path~readonlyinlet+payload=readioin{io;payload;path;tmp_path}letcloset=Io.closet.ioletreadonlyt=Io.readonlyt.ioletpayloadt=t.payloadletreloadt=letopenResult_syntaxinifnot@@Io.readonlyt.iothenError`Rw_not_allowedelselet*()=Io.closet.ioinlet*io=Io.open_~path:t.path~readonly:trueint.io<-io;let+payload=readioint.payload<-payloadletread_payload~path=letopenResult_syntaxinlet*io=Io.open_~path~readonly:trueinlet*payload=readioinlet+()=Io.closeioinpayloadletread_raw_payload~path=letopenResult_syntaxinlet*io=Io.open_~path~readonly:trueinlet*string=Io.read_all_to_stringioinlet*payload=Serde.raw_of_bin_stringpathstringinlet+()=Io.closeioinpayloadletfsynct=Io.fsynct.ioendmoduleUpper=Make(Serde.Upper)moduleVolume=Make(Serde.Volume)