%%%------------------------------------------------------------------- %%% File: swab_tar.erl %%% @author Eric Pailleau %%% @copyright 2014 crownedgrouse.com %%% @doc %%% Tar module for swab %%% @end %%% %%% Permission to use, copy, modify, and/or 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. %%% %%% Created : 2014-08-11 %%%------------------------------------------------------------------- -module(swab_tar). -export([fakeroot/1, change_user/2, change_group/2]). %%------------------------------------------------------------------------- %%@doc Replace Uid and User %%@end %%------------------------------------------------------------------------- -spec change_user(binary(), tuple()) -> binary(). change_user(Data, {Uid, User}) when is_integer(Uid), is_list(User) -> change_user(Data, {Uid, User}, []). change_user(<<>>, {_, _}, Iolist) -> list_to_binary(lists:flatten(Iolist)); change_user(<>, {_, _}, Iolist) when size(Rest)=< 512 -> list_to_binary(lists:flatten(Iolist ++ [Rest])); change_user(<>, {Uid, User}, Iolist) -> {Skip, NewHeader} = tar_change_header_user(Header, {Uid, User}), case Skip of 0 -> change_user(Rest, {Uid, User}, Iolist ++ [NewHeader]) ; _ -> <> = Rest, change_user(Next, {Uid, User}, Iolist ++ [NewHeader, File]) end. %%------------------------------------------------------------------------- %%@doc Replace Gid and Group %%@end %%------------------------------------------------------------------------- -spec change_group(binary(), tuple()) -> binary(). change_group(Data, {Gid, Group}) when is_integer(Gid), is_list(Group) -> change_group(Data, {Gid, Group}, []). change_group([], {_, _}, Iolist) -> list_to_binary(lists:flatten(Iolist)); change_group(<>, {_, _}, Iolist) when size(Rest)=< 512 -> list_to_binary(lists:flatten(Iolist ++ [Rest])); change_group(<>, {Gid, Group}, Iolist) -> {Skip, NewHeader} = tar_change_header_group(Header, {Gid, Group}), case Skip of 0 -> change_group(Rest, {Gid, Group}, Iolist ++ [NewHeader]) ; _ -> <> = Rest, change_group(Next, {Gid, Group}, Iolist ++ [NewHeader, File]) end. %%------------------------------------------------------------------------- %%@doc Replace Uid/Gid and User/Group to 0/root %%@end %%------------------------------------------------------------------------- -spec fakeroot(binary()) -> binary(). fakeroot(Data) -> fakeroot(Data, []). -spec fakeroot(binary(), iolist()) -> binary(). fakeroot(<<>>, Iolist) -> list_to_binary(lists:flatten(Iolist)); fakeroot(<>, Iolist) when size(Rest)=< 512 -> list_to_binary(lists:flatten(Iolist ++ [Rest])); fakeroot(<>, Iolist) -> {Skip, NewHeader1} = tar_change_header_user(Header, {0,"root"}), {Skip, NewHeader} = tar_change_header_group(NewHeader1, {0,"root"}), case Skip of 0 -> fakeroot(Rest, Iolist ++ [NewHeader]) ; _ -> <> = Rest, fakeroot(Next, Iolist ++ [NewHeader, File]) end. %%------------------------------------------------------------------------- %%@doc %% Return the length to skip in the file (padded Size), and the new header %% A match against "ustar" string is done to be sure a valid header is being treated, %% or otherwise remaining padding zeroes at end of file. %%@end %%------------------------------------------------------------------------- -spec tar_change_header_user(binary(), tuple()) -> tuple(). tar_change_header_user(<>, {Uid, UserName}) -> SkipSize = list_to_integer(binary_to_list(Size),8), Skip = ((SkipSize + 512 - 1) div 512) * 512, NewUser = list_to_binary([UserName,binary:copy(<<0>>, 32 - length(UserName))]), 32 = byte_size(NewUser), %Assertion. H = list_to_binary(lists:flatten([Start,to_octal(Uid, 8),G, Size,Middle,"ustar",<<0>>,"00",NewUser,Group,End])), 512 = byte_size(H), %Assertion. <> = H, New = <>, ChksumString = to_octal(checksum(New), 6, [0,$\s]), {Skip,list_to_binary([Before,ChksumString,After])}; tar_change_header_user(X,_) -> tar_change_header(X). %%------------------------------------------------------------------------- %%@doc %% Return the length to skip in the file (padded Size), and the new header %% A match against "ustar" string is done to be sure a valid header is being treated, %% or otherwise remaining padding zeroes at end of file. %%@end %%------------------------------------------------------------------------- -spec tar_change_header_group(binary(), tuple()) -> tuple(). tar_change_header_group(<>, {Gid, GroupName}) -> SkipSize = list_to_integer(binary_to_list(Size),8), Skip = ((SkipSize + 512 - 1) div 512) * 512, NewGroup = list_to_binary([GroupName,binary:copy(<<0>>, 32 - length(GroupName))]), 32 = byte_size(NewGroup), %Assertion. H = list_to_binary(lists:flatten([Start,U,to_octal(Gid, 8), Size,Middle,"ustar",<<0>>,"00",User,NewGroup,End])), 512 = byte_size(H), %Assertion. <> = H, New = <>, ChksumString = to_octal(checksum(New), 6, [0,$\s]), {Skip,list_to_binary([Before,ChksumString,After])}; tar_change_header_group(X,_) -> tar_change_header(X). %%------------------------------------------------------------------------- %%@doc Remaining padding zeroes at end of file. %%@end %%------------------------------------------------------------------------- -spec tar_change_header(binary()) -> tuple(). tar_change_header(X) -> case binary:copy(<<0>>, size(X)) of X -> {0, X} ; _ -> throw(invalid), {0, X} % Neither valid header, nor zeroes padding. end. %%------------------------------------------------------------------------- %%@doc Checksum of Tar Header %%@end %%------------------------------------------------------------------------- -spec checksum(binary()) -> integer(). checksum(H) -> checksum(H, 0). checksum(<>, Sum) -> checksum(T, Sum+A+B+C+D+E+F+G+H); checksum(<>, Sum) -> checksum(T, Sum+A); checksum(<<>>, Sum) -> Sum. %%------------------------------------------------------------------------- %%@doc Cast to Octal %%@end %%------------------------------------------------------------------------- -spec to_octal(integer(), integer()) -> list(). to_octal(Int, Count) when Count > 1 -> to_octal(Int, Count-1, [0]). -spec to_octal(integer(), integer(), list()) -> list(). to_octal(_, 0, Result) -> Result; to_octal(Int, Count, Result) -> to_octal(Int div 8, Count-1, [Int rem 8 + $0|Result]).