Skip to content

Instantly share code, notes, and snippets.

@maxlapshin
Last active January 2, 2016 19:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save maxlapshin/8353034 to your computer and use it in GitHub Desktop.
Save maxlapshin/8353034 to your computer and use it in GitHub Desktop.
Catalog -> rpm without librpm.
#!/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