Last active
January 2, 2016 19:49
-
-
Save maxlapshin/8353034 to your computer and use it in GitHub Desktop.
Catalog -> rpm without librpm.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env escript | |
| -mode(compile). | |
| -include_lib("kernel/include/file.hrl"). | |
| main(["readcpio", Path]) -> | |
| dump_cpio(Path); | |
| main(["show", Path]) -> | |
| {ok, F} = file:open(Path, [binary, read, raw]), | |
| {ok, RPMLead} = file:read(F, 96), | |
| <<Magic:4/binary, Major, Minor, Type:16, Arch:16, Name0:66/binary, OS:16, SigType:16, _Reserve:16/binary>> = RPMLead, | |
| <<16#ed, 16#ab, 16#ee, 16#db>> = Magic, | |
| 3 = Major, | |
| 0 = Minor, | |
| 0 = Type, | |
| 1 = OS, % Linux | |
| 5 = SigType, % new "Header-style" signatures | |
| [Name, _] = binary:split(Name0, <<0>>), | |
| io:format("[Lead] OK\n"), | |
| io:format("arch: ~p, name: ~p\n", [Arch, Name]), | |
| {ok, SigPos} = file:position(F, cur), | |
| io:format("[Signature] OK at offset ~B\n", [SigPos]), | |
| {ok, {Sig, SigData, SigLen}} = read_signatures(F), | |
| io:format("signature: pad:~B, ~p\n---\n~p\n", [SigLen rem 8, Sig, SigData]), | |
| file:read(F, SigLen rem 8), | |
| {ok, HeaderStart} = file:position(F,cur), | |
| io:format("[Header] OK at offset ~B\n", [HeaderStart]), | |
| {ok, {_Entries, D, _}} = read_headers(F), | |
| io:format("data: ~p\n", [D]), | |
| {ok, Pos} = file:position(F,cur), | |
| {ok, Eof} = file:position(F,eof), | |
| io:format("Payload offset: ~B, size: ~B, signed_size: ~B, payload_size: ~B\n", [Pos, Eof - Pos, | |
| Eof - HeaderStart, hd(proplists:get_value(signature_size,SigData))]), | |
| {ok, HeaderBin} = file:pread(F, HeaderStart, Pos - HeaderStart), | |
| {ok, PayloadStart} = file:pread(F, Pos, 40), | |
| % {ok, CPIO} = file:pread(F, Pos, Eof - Pos), | |
| % file:write_file("out.cpio.xz", CPIO), | |
| io:format("Payload starts from: ~240p\n", [PayloadStart]), | |
| {ok, Signed} = file:pread(F, HeaderStart, Eof - HeaderStart), | |
| MD5_ = crypto:hash(md5, Signed), | |
| MD5 = hd(proplists:get_value(md5_header, SigData)), | |
| MD5_ = MD5, | |
| case proplists:get_value(sha1_header, SigData) of | |
| [SHA1] -> | |
| SHA1_ = hex(crypto:hash(sha, [HeaderBin])), | |
| io:format("SHA\n~s\n~s\n", [SHA1, SHA1_]); | |
| undefined -> | |
| ok | |
| end, | |
| % io:format("CPIO offset on pos ~B, size: ~p\n", [Pos, Eof - Pos]), | |
| % {ok, C} = file:open("arch.cpio", [binary,write,raw]), | |
| % {ok, Cpio} = file:pread(F, Pos, Eof - Pos), | |
| % file:write(C, Cpio), | |
| ok; | |
| main(["write", RPM | Dirs]) when length(Dirs) > 0 -> | |
| write(RPM, Dirs); | |
| main([]) -> | |
| io:format("show path\nwrite path dir\n"), | |
| ok. | |
| -record(entry, { | |
| tag, | |
| tag_id, | |
| type, | |
| offset, | |
| size = 0, | |
| count | |
| }). | |
| header_tag_to_sig(name) -> signature_size; | |
| header_tag_to_sig(release) -> pgp_header; | |
| header_tag_to_sig(summary) -> md5_header; | |
| header_tag_to_sig(buildhost) -> signature_payloadsize; | |
| header_tag_to_sig(T) -> T. | |
| read_signatures(F) -> | |
| {ok, {Sig0, SigData0, SigLen}} = read_headers(F), | |
| Sig = [E#entry{tag = header_tag_to_sig(T)} || #entry{tag = T} = E <- Sig0], | |
| SigData = [{header_tag_to_sig(T),V} || {T,V} <- SigData0], | |
| {ok, {Sig, SigData, SigLen}}. | |
| read_headers(F) -> | |
| % {ok, 96} = file:position(F, cur), | |
| {ok, <<16#8e, 16#ad, 16#e8, 16#01, 0:32, EntryCount:32, Bytes:32>>} = file:read(F, 4*4), | |
| {ok, IndexPos} = file:position(F,cur), | |
| {ok, IndexEntries} = file:read(F, 16*EntryCount), | |
| Entries1 = read_index_entries(IndexEntries, EntryCount), | |
| {ok, Data} = file:read(F, Bytes), | |
| D = read_entries(Data, Entries1), | |
| Entries2 = lists:zipwith(fun | |
| (#entry{type = bin} = E, {_, Bin}) -> E#entry{size = iolist_size(Bin)}; | |
| (#entry{type = string_array} = E, {_, Array}) -> E#entry{size = lists:sum([size(S)+1 || S <- Array])}; | |
| (#entry{type = string} = E, {_, [S]}) -> E#entry{size = size(S)+1}; | |
| (#entry{type = i18n_string} = E, {_, [S]}) -> E#entry{size = size(S)+1}; | |
| (#entry{type = int32} = E, {_, I}) when is_integer(I) -> E#entry{size = 4}; | |
| (#entry{type = int32} = E, {_, I}) when is_list(I) -> E#entry{size = 4*length(I)}; | |
| (#entry{type = int16} = E, {_, I}) when is_integer(I) -> E#entry{size = 2}; | |
| (#entry{type = int16} = E, {_, I}) when is_list(I) -> E#entry{size = 2*length(I)} | |
| end, Entries1, D), | |
| io:format("index entries (entry_count(il): ~B, byte_count(dl): ~B, offset: ~B, index_size: ~B):\n", [EntryCount, Bytes, IndexPos, 16*EntryCount]), | |
| io:format("~8.. s ~8.. s ~5.. s ~20.. s [type]\n", ["offset", "next", "count", "tag(tagid)"]), | |
| [io:format("~8.. B ~8.. B ~5.. B ~20.. s(~4.. B) [~p]\n", [Offset, Offset+Size, Count,Tag,TagId,Type]) || | |
| #entry{tag = Tag, size = Size, tag_id = TagId, type = Type, offset = Offset, count = Count} <- Entries2], | |
| io:format("\n"), | |
| % Lead size + magic + reserve + entries + bytes + 16*entries | |
| % BaseOffset = 96 + 4*4 + 16*EntryCount, | |
| % {ok, BaseOffset} = file:position(F, cur), | |
| {ok, {Entries2, D, Bytes}}. | |
| read_index_entries(<<>>, 0) -> | |
| []; | |
| read_index_entries(<<Tag:32, Type:32, Offset:32, Count:32, Bin/binary>>, Cnt) -> | |
| % io:format("index. tag: ~p, type: ~p, offset: ~p, count: ~p, rest: ~200p\n", [read_tag(Tag), read_type(Type), Offset, Count, h(Bin)]), | |
| Entry = #entry{tag = read_tag(Tag), tag_id = Tag, type = read_type(Type), offset = Offset, count = Count}, | |
| [Entry|read_index_entries(Bin, Cnt-1)]. | |
| % h(<<Bin:20/binary, _/binary>>) -> Bin; | |
| % h(<<Bin/binary>>) -> Bin. | |
| read_type(0) -> null; | |
| read_type(1) -> char; | |
| read_type(2) -> int8; | |
| read_type(3) -> int16; | |
| read_type(4) -> int32; | |
| read_type(5) -> int64; | |
| read_type(6) -> string; | |
| read_type(7) -> bin; | |
| read_type(8) -> string_array; | |
| read_type(9) -> i18n_string. | |
| read_tag(62) -> header_signatures; | |
| read_tag(63) -> headerimmutable; | |
| read_tag(100) -> headeri18ntable; | |
| read_tag(268) -> rsa_header; | |
| read_tag(269) -> sha1_header; | |
| read_tag(1000) -> name; % size for signature | |
| read_tag(1001) -> version; | |
| read_tag(1002) -> release; % pgp for signature | |
| read_tag(1004) -> summary; % md5 for signature | |
| read_tag(1005) -> description; | |
| read_tag(1006) -> buildtime; | |
| read_tag(1007) -> buildhost; % this is payloadsize for signature | |
| read_tag(1009) -> size; | |
| read_tag(1010) -> distribution; | |
| read_tag(1011) -> vendor; | |
| read_tag(1014) -> license; | |
| read_tag(1015) -> packager; | |
| read_tag(1016) -> group; | |
| read_tag(1020) -> url; | |
| read_tag(1021) -> os; | |
| read_tag(1022) -> arch; | |
| read_tag(1028) -> filesizes; | |
| read_tag(1030) -> filemodes; | |
| read_tag(1033) -> filerdevs; | |
| read_tag(1034) -> filemtimes; | |
| read_tag(1035) -> filedigests; | |
| read_tag(1036) -> filelinktos; | |
| read_tag(1037) -> fileflags; | |
| read_tag(1039) -> fileusername; | |
| read_tag(1040) -> filegroupname; | |
| read_tag(1044) -> sourcerpm; | |
| read_tag(1045) -> fileverifyflags; | |
| read_tag(1047) -> providename; | |
| read_tag(1048) -> requireflags; | |
| read_tag(1049) -> requirename; | |
| read_tag(1050) -> requireversion; | |
| read_tag(1064) -> rpmversion; | |
| read_tag(1080) -> changelogtime; | |
| read_tag(1081) -> changelogname; | |
| read_tag(1082) -> changelogtext; | |
| read_tag(1090) -> obsoletename; | |
| read_tag(1095) -> filedevices; | |
| read_tag(1096) -> fileinodes; | |
| read_tag(1097) -> filelangs; | |
| read_tag(1112) -> provideflags; | |
| read_tag(1113) -> provideversion; | |
| read_tag(1116) -> dirindexes; | |
| read_tag(1117) -> basenames; | |
| read_tag(1118) -> dirnames; | |
| read_tag(1122) -> optflags; | |
| read_tag(1124) -> payloadformat; | |
| read_tag(1125) -> payloadcompressor; | |
| read_tag(1126) -> payloadflags; | |
| read_tag(1132) -> platform; | |
| read_tag(1140) -> filecolors; | |
| read_tag(1141) -> fileclass; | |
| read_tag(1142) -> classdict; | |
| read_tag(1143) -> filedependsx; | |
| read_tag(1144) -> filedependsn; | |
| read_tag(1145) -> filedependsdict; | |
| read_tag(5011) -> filedigestalgo; | |
| read_tag(I) -> list_to_atom(integer_to_list(I)). | |
| read_entries(_F, []) -> | |
| []; | |
| read_entries(F, [#entry{tag = Tag, offset = O, count = Count, type = Type}|Entries]) -> | |
| Values = read_values(F, O, Type, Count), | |
| [{Tag,Values}|read_entries(F, Entries)]. | |
| read_values(_F, _Offset, _, 0) -> | |
| []; | |
| read_values(F, Offset, int32, Count) -> | |
| % {ok, <<I:32>>} = file:pread(F, Offset, 4), | |
| <<_:Offset/binary, I:32, _After/binary>> = F, | |
| [I|read_values(F, Offset + 4, int32, Count - 1)]; | |
| read_values(F, Offset, int16, Count) -> | |
| % {ok, <<I:32>>} = file:pread(F, Offset, 4), | |
| <<_:Offset/binary, I:16, _After/binary>> = F, | |
| [I|read_values(F, Offset + 2, int16, Count - 1)]; | |
| read_values(F, Offset, string, _Count) -> | |
| {EOL, 1} = binary:match(F, <<0>>, [{scope,{Offset,size(F) - Offset}}]), | |
| Len = EOL - Offset, | |
| <<_:Offset/binary, String:Len/binary, _/binary>> = F, | |
| [String]; | |
| read_values(F, _Offset, i18n_string, _Count) -> | |
| [String, _] = binary:split(F, <<0>>), | |
| [String]; | |
| read_values(F, Offset, bin, Count) -> | |
| % {ok, _Bin} = file:pread(F, Offset, Count), | |
| % Bin = {bin,Offset,Count}, | |
| <<_:Offset/binary, Bin:Count/binary, _/binary>> = F, | |
| [Bin]; | |
| read_values(F, Offset, string_array, Count) -> | |
| extract_n_strings(F, Offset, Count); | |
| read_values(_, _, _, _) -> | |
| [unknown]. | |
| extract_n_strings(_F, _Offset, 0) -> []; | |
| extract_n_strings(F, Offset, Count) -> | |
| {EOL, 1} = binary:match(F, <<0>>, [{scope,{Offset,size(F) - Offset}}]), | |
| Len = EOL - Offset, | |
| <<_:Offset/binary, String:Len/binary, _/binary>> = F, | |
| [String|extract_n_strings(F, Offset + Len + 1, Count - 1)]. | |
| % $$\ $$\ $$$$$$$\ $$$$$$\ $$$$$$$$\ $$$$$$$$\ | |
| % $$ | $\ $$ |$$ __$$\ \_$$ _|\__$$ __|$$ _____| | |
| % $$ |$$$\ $$ |$$ | $$ | $$ | $$ | $$ | | |
| % $$ $$ $$\$$ |$$$$$$$ | $$ | $$ | $$$$$\ | |
| % $$$$ _$$$$ |$$ __$$< $$ | $$ | $$ __| | |
| % $$$ / \$$$ |$$ | $$ | $$ | $$ | $$ | | |
| % $$ / \$$ |$$ | $$ |$$$$$$\ $$ | $$$$$$$$\ | |
| % \__/ \__|\__| \__|\______| \__| \________| | |
| % magic() -> | |
| % <<16#8e, 16#ad, 16#e8, 16#01, 0:32>>. | |
| write(RPMPath, Dirs0) -> | |
| % It is a problem: how to store directory names. RPM requires storing them in "/etc/" and "flussonic.conf" | |
| % cpio required: "etc/flussonic.conf" | |
| Dirs = lists:map(fun | |
| ("./" ++ Dir) -> Dir; | |
| ("/" ++ _ = Dir) -> error({absoulte_dir_not_allowed,Dir}); | |
| (Dir) -> Dir | |
| end, Dirs0), | |
| {match, [Name, Version, Arch]} = re:run(RPMPath, "([^-]+)-(.+)\\.([^\\.]+)\\.rpm", [{capture,all_but_first,binary}]), | |
| % Need to sort files because mapFind will make bsearch to find them | |
| Files = lists:sort(lists:flatmap(fun(Dir) -> list(Dir) end, Dirs)), | |
| CPIO = cpio(Files), | |
| Header = header([{name,Name},{version,Version},{arch,Arch},{size,iolist_size(CPIO)}], Files), | |
| MD5 = crypto:hash(md5, [Header, CPIO]), | |
| Signature = [ | |
| {sha1_header,hex(crypto:hash(sha, [Header]))}, | |
| {signature_size,iolist_size(Header) + iolist_size(CPIO)},{signature_md5,{bin,MD5}} | |
| ], | |
| {ok, F} = file:open(RPMPath, [binary, write, raw]), | |
| ok = file:write(F, rpm_lead(Name)), | |
| ok = file:write(F, signatures(Signature)), | |
| ok = file:write(F, Header), | |
| % {ok, CpioPos} = file:position(F, cur), | |
| % io:format("Write cpio at offset ~B\n", [CpioPos]), | |
| ok = file:write(F, CPIO), | |
| ok. | |
| hex(Bin) -> | |
| iolist_to_binary(string:to_lower(lists:flatten([io_lib:format("~2.16.0B", [I]) || <<I>> <= Bin]))). | |
| rpm_lead(Name) -> | |
| Magic = <<16#ed, 16#ab, 16#ee, 16#db>>, | |
| Major = 3, | |
| Minor = 0, | |
| Type = 0, | |
| Arch = 1, | |
| OS = 1, % Linux | |
| SigType = 5, % new "Header-style" signatures | |
| Name0 = iolist_to_binary([Name, binary:copy(<<0>>, 66 - size(Name))]), | |
| Reserve = binary:copy(<<0>>, 16), | |
| Lead = <<Magic:4/binary, Major, Minor, Type:16, Arch:16, Name0:66/binary, OS:16, SigType:16, Reserve:16/binary>>, | |
| 96 = size(Lead), | |
| Lead. | |
| signatures(Headers) -> | |
| {_Magic,Index0, Data0} = pack_header(Headers), | |
| HeaderSign = <<0,0,0,62, 0,0,0,7, (-(iolist_size(Index0)+16)):32/signed, 0,0,0,16>>, | |
| {Magic,Index, Data} = magic(length(Headers)+1, [pack_index({header_signatures,bin,iolist_size(Data0),size(HeaderSign)})|Index0], [Data0,HeaderSign]), | |
| Pad = pad8(Data), | |
| % io:format("Write signature index_size:~B, header_size:~B, pad:~B\n", [iolist_size(Index), iolist_size(Data), iolist_size(Pad)]), | |
| [Magic, Index, [Data,Pad]]. | |
| pad8(Data) -> pad(Data, 8). | |
| % pad4(Data) -> pad(Data, 4). | |
| pad(Data, N) -> | |
| Pad = binary:copy(<<0>>, iolist_size(Data) rem N), | |
| Pad. | |
| list(Dir) -> | |
| lists:filter(fun(Path) -> | |
| {ok, #file_info{type = T}} = file:read_file_info(Path), | |
| T == regular | |
| end, filelib:fold_files(Dir, ".*", true, fun(P,L) -> [list_to_binary(P)|L] end, [])). | |
| utc({{_Y,_Mon,_D},{_H,_Min,_S}} = DateTime) -> | |
| calendar:datetime_to_gregorian_seconds(DateTime) - calendar:datetime_to_gregorian_seconds({{1970,1,1}, {0,0,0}}). | |
| dump_cpio(Path) -> | |
| {ok, C} = file:read_file(Path), | |
| dump_cpio0(C). | |
| from_b(<<Bin:8/binary>>) -> | |
| list_to_integer(binary_to_list(Bin),16). | |
| cpio_pad4(I) when I rem 4 == 0 -> 0; | |
| cpio_pad4(I) -> 4 - (I rem 4). | |
| dump_cpio0(<<>>) -> | |
| ok; | |
| dump_cpio0(<<"070701", Inode:8/binary, Mode:8/binary, _UID:8/binary, _GID:8/binary, Nlinks:8/binary, Mtime:8/binary, | |
| Fsize:8/binary, Major:8/binary, Minor:8/binary, _RMajor:8/binary, _RMinor:8/binary, NameLen:8/binary, _Check:8/binary, | |
| Rest1/binary>>) -> | |
| NameLen0 = from_b(NameLen) - 1, | |
| Size = from_b(Fsize), | |
| Pad1 = cpio_pad4(110 + NameLen0 + 1), | |
| Pad2 = cpio_pad4(Size), | |
| <<Name:NameLen0/binary, 0, _:Pad1/binary, _Body:Size/binary, _:Pad2/binary, Rest2/binary>> = Rest1, | |
| Meta = [{inode,from_b(Inode)},{mode,from_b(Mode)}, | |
| {nlinks,from_b(Nlinks)},{mtime,from_b(Mtime)},{fsize,from_b(Fsize)},{major,from_b(Major)},{minor,from_b(Minor)},{name,Name}], | |
| io:format("~240p, pad: ~p, ~p\n",[Meta, Pad1, Pad2]), | |
| dump_cpio0(Rest2). | |
| to_b(I) when is_integer(I) -> | |
| iolist_to_binary(string:to_lower(lists:flatten(io_lib:format("~8.16.0B", [I])))). | |
| cpio([]) -> | |
| cpio_pack("TRAILER!!!", 0, 0, 0); | |
| cpio([Path|Paths]) -> | |
| {ok, #file_info{inode = Inode, size = Size, mode = Mode}} = file:read_file_info(Path), | |
| Rest = cpio(Paths), | |
| Pack1 = cpio_pack(<<"/", Path/binary>>, Size, Inode, Mode), | |
| Pad2 = binary:copy(<<0>>, cpio_pad4(Size)), | |
| {ok, Bin} = file:read_file(Path), | |
| Pack1 ++ [Bin, Pad2] ++ Rest. | |
| now_s() -> | |
| {Mega, Sec, _} = os:timestamp(), | |
| Mega*1000000 + Sec. | |
| cpio_pack(Name, Size, Inode, Mode) -> | |
| Nlinks = case Inode of | |
| 0 -> 0; | |
| _ -> 1 | |
| end, | |
| Major = case Inode of | |
| 0 -> 0; | |
| _ -> 263 | |
| end, | |
| ["070701", to_b(Inode), to_b(Mode), to_b(0), to_b(0), to_b(Nlinks), to_b(now_s()), to_b(Size), to_b(Major), to_b(0), to_b(Major), to_b(0), | |
| to_b(iolist_size(Name)+1), to_b(0), Name, 0, binary:copy(<<0>>, cpio_pad4(iolist_size(Name) + 1 + 110))]. | |
| header(Addons, Files) -> | |
| Infos = [begin | |
| {ok, Info} = file:read_file_info(File), | |
| Info | |
| end || File <- Files], | |
| Dirs0 = lists:usort([filename:dirname(F) || F <- Files]), | |
| Dirs = lists:zip(Dirs0, lists:seq(0,length(Dirs0)-1)), | |
| Headers = [ | |
| {headeri18ntable, [<<"C">>]} | |
| ] ++ | |
| Addons ++ | |
| [ | |
| {summary, <<"Video streaming server">>}, | |
| {description, <<"Flussonic is a highly performant video streaming server\nwith graphical configurator, stats, reports, etc.">>}, | |
| {buildtime, utc(erlang:universaltime())}, | |
| {buildhost, <<"dev.flussonic.com">>}, | |
| {vendor, <<"Flussonic, LLC">>}, | |
| {license, <<"EULA">>}, | |
| {packager, <<"Flussonic, LLC">>}, | |
| {group, <<"Servers/Video">>}, | |
| {url, <<"http://www.flussonic.com/">>}, | |
| {os, <<"linux">>}, | |
| {filesizes, [Size || #file_info{size = Size} <- Infos]}, | |
| {filemodes, {int16, [Mode || #file_info{mode = Mode} <- Infos]}}, | |
| {filemtimes, [utc(Mtime) || #file_info{mtime = Mtime} <- Infos]}, | |
| {fileflags, [2 || _ <- Files]}, | |
| {fileusername, [<<"root">> || _ <- Files]}, | |
| {filegroupname, [<<"root">> || _ <- Files]}, | |
| {filelinktos, [<<>> || _ <- Files]}, | |
| {filerdevs, [0 || _ <- Files]}, | |
| % {requirename,[<<"/bin/bash">>, <<"rpmlib(CompressedFileNames)">>, | |
| % <<"rpmlib(FileDigests)">>, | |
| % <<"rpmlib(PayloadFilesHavePrefix)">>, | |
| % <<"rpmlib(PayloadIsXz)">>]}, | |
| % {requireversion,[<<>>,<<"3.0.4-1">>,<<"4.6.0-1">>,<<"4.0-1">>,<<"5.2-1">>]}, | |
| {rpmversion, <<"4.8.0">>}, | |
| {fileinodes, [inode(F) || F <- Files]}, | |
| {filelangs, [<<>> || _ <- Files]}, | |
| {dirindexes, [proplists:get_value(filename:dirname(F),Dirs) || F <- Files]}, | |
| {basenames, [filename:basename(File) || File <- Files]}, | |
| {dirnames, [<<"/", Dir/binary, "/">> || {Dir, _} <- Dirs]}, | |
| {payloadformat, <<"cpio">>}, | |
| % {payloadcompressor, <<"xz">>}, | |
| {payloadflags, <<"2">>}, | |
| {platform, <<"x86_64-redhat-linux-gnu">>}, | |
| {filecolors, [0 || _ <- Files]}, | |
| {fileclass, [1 || _ <- Files]}, | |
| {classdict, [<<>>, <<"file">>]}, | |
| {filedependsx, [0 || _ <- Files]}, | |
| {filedependsn, [0 || _ <- Files]}, | |
| {filedigestalgo, [8]} | |
| ], | |
| {_,Index0, Data0} = pack_header(Headers), | |
| % Data1 = [Data0, align(16, iolist_size(Data0))], | |
| Data1 = Data0, | |
| % io:format("aligned offset of magic: ~B, ~B\n", [iolist_size(Data1), iolist_size(Data1) rem 16]), | |
| Immutable = <<0,0,0,63, 0,0,0,7, (-(iolist_size(Index0)+16)):32, 0,0,0,16>>, | |
| {Magic, Index, Data} = magic(length(Headers)+1, [pack_index({headerimmutable,bin,iolist_size(Data1),size(Immutable)})|Index0], [Data1,Immutable]), | |
| % io:format("header. index: ~B entries, ~B bytes, data: ~B bytes\n", [length(Headers)+1, iolist_size(Index), iolist_size(Data)]), | |
| [Magic, Index, Data]. | |
| inode(File) -> | |
| {ok, #file_info{inode = Inode}} = file:read_file_info(File), | |
| Inode. | |
| pack_header(Headers) -> | |
| {Index, Data} = pack_header0(Headers, [], [], 0), | |
| magic(length(Headers), Index, Data). | |
| magic(EntryCount, Index, Data) -> | |
| Bytes = iolist_size(Data), | |
| Magic = <<16#8e, 16#ad, 16#e8, 16#01, 0:32, EntryCount:32, Bytes:32>>, | |
| % io:format("pack magic: entries:~B, bytes:~B\n", [EntryCount, Bytes]), | |
| {Magic,Index, Data}. | |
| pack_header0([], Index, Data, _) -> | |
| {lists:reverse([pack_index(I) || I <- Index]), lists:reverse(Data)}; | |
| pack_header0([{Key,{bin,Value}}|Headers], Index, Data, Offset) when is_binary(Value) -> | |
| pack_header0(Headers, [{Key,bin,Offset,size(Value)}|Index], [Value|Data], Offset + size(Value)); | |
| pack_header0([{Key,Value}|Headers], Index, Data, Offset) when is_integer(Value) -> | |
| Align = align(4, Offset), | |
| % Align = <<>>, | |
| pack_header0(Headers, [{Key,int32,Offset+size(Align),1}|Index], [<<Value:32>>, Align|Data], Offset + size(Align) + 4); | |
| pack_header0([{Key,{int16, Values}}|Headers], Index, Data, Offset) -> | |
| Align = align(2, Offset), | |
| % Align = <<>>, | |
| pack_header0(Headers, [{Key,int16,Offset+size(Align),length(Values)}|Index], [[<<V:16>> || V <- Values],Align|Data], Offset + size(Align) + 2*length(Values)); | |
| pack_header0([{Key,[Value|_] = Values}|Headers], Index, Data, Offset) when is_integer(Value) -> | |
| Align = align(4, Offset), | |
| % Align = <<>>, | |
| pack_header0(Headers, [{Key,int32,Offset+size(Align),length(Values)}|Index], [[<<V:32>> || V <- Values],Align|Data], Offset + size(Align) + 4*length(Values)); | |
| pack_header0([{Key,Value}|Headers], Index, Data, Offset) when is_binary(Value) -> | |
| String = <<Value/binary, 0>>, | |
| Pad = <<>>, | |
| pack_header0(Headers, [{Key,string,Offset,1}|Index], [Pad,String|Data], Offset + size(String)+size(Pad)); | |
| pack_header0([{Key,[Value|_] = Values}|Headers], Index, Data, Offset) when is_binary(Value) -> | |
| Size = lists:sum([size(V) + 1 || V <- Values]), | |
| pack_header0(Headers, [{Key,string_array,Offset,length(Values)}|Index], [[<<V/binary, 0>> || V <- Values]|Data], Offset + Size). | |
| align(N, Offset) when Offset rem N == 0 -> <<>>; | |
| align(N, Offset) -> binary:copy(<<0>>, N - (Offset rem N)). | |
| pack_index({Tag, Type, Offset, Count}) -> | |
| <<(write_tag(Tag)):32, (write_type(Type)):32, Offset:32, Count:32>>. | |
| write_tag(header_signatures) -> 62; | |
| write_tag(headerimmutable) -> 63; | |
| write_tag(headeri18ntable) -> 100; | |
| write_tag(sha1_header) -> 269; | |
| write_tag(signature_size) -> 1000; | |
| write_tag(name) -> 1000; | |
| write_tag(version) -> 1001; | |
| write_tag(signature_md5) -> 1004; | |
| write_tag(summary) -> 1004; | |
| write_tag(description) -> 1005; | |
| write_tag(buildtime) -> 1006; | |
| write_tag(signature_payloadsize) -> 1007; | |
| write_tag(buildhost) -> 1007; | |
| write_tag(size) -> 1009; | |
| write_tag(vendor) -> 1011; | |
| write_tag(license) -> 1014; | |
| write_tag(packager) -> 1015; | |
| write_tag(group) -> 1016; | |
| write_tag(url) -> 1020; | |
| write_tag(os) -> 1021; | |
| write_tag(arch) -> 1022; | |
| write_tag(filesizes) -> 1028; | |
| write_tag(filemodes) -> 1030; | |
| write_tag(filerdevs) -> 1033; | |
| write_tag(filemtimes) -> 1034; | |
| write_tag(filelinktos) -> 1036; | |
| write_tag(fileflags) -> 1037; | |
| write_tag(fileusername) -> 1039; | |
| write_tag(filegroupname) -> 1040; | |
| write_tag(requirename) -> 1049; | |
| write_tag(requireversion) -> 1050; | |
| write_tag(rpmversion) -> 1064; | |
| write_tag(fileinodes) -> 1096; | |
| write_tag(filelangs) -> 1097; | |
| write_tag(dirindexes) -> 1116; | |
| write_tag(basenames) -> 1117; | |
| write_tag(dirnames) -> 1118; | |
| write_tag(payloadformat) -> 1124; | |
| write_tag(payloadcompressor) -> 1125; | |
| write_tag(payloadflags) -> 1126; | |
| write_tag(platform) -> 1132; | |
| write_tag(filecolors) -> 1140; | |
| write_tag(fileclass) -> 1141; | |
| write_tag(classdict) -> 1142; | |
| write_tag(filedependsx) -> 1143; | |
| write_tag(filedependsn) -> 1144; | |
| write_tag(filedigestalgo) -> 5011. | |
| write_type(int16) -> 3; | |
| write_type(int32) -> 4; | |
| write_type(bin) -> 7; | |
| write_type(string_array) -> 8; | |
| write_type(string) -> 6. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment