Skip to content

Commit

Permalink
Allow setting file_info opts on :hex_erl_tar.add
Browse files Browse the repository at this point in the history
Note: no os:system_time/1 on OTP 17, so we default to t=0.
  • Loading branch information
wojtekmach committed Jan 29, 2018
1 parent d317671 commit afe4762
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 22 deletions.
10 changes: 6 additions & 4 deletions lib/hex/tar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -154,23 +154,25 @@ defmodule Hex.Tar do
defp file_op(:read2, {fd, size}), do: :file.read(fd, size)
defp file_op(:close, _fd), do: :ok

@tar_opts [atime: 0, mtime: 0, ctime: 0, uid: 0, gid: 0]

defp add_files(tar, files) do
Enum.each(files, fn
{name, contents, mode} ->
:ok = :hex_erl_tar.add(tar, contents, string_to_charlist(name), mode, [])
:ok = :hex_erl_tar.add(tar, contents, string_to_charlist(name), mode, @tar_opts)

{name, contents} ->
:ok = :hex_erl_tar.add(tar, contents, string_to_charlist(name), [])
:ok = :hex_erl_tar.add(tar, contents, string_to_charlist(name), @tar_opts)

name ->
case file_lstat(name) do
{:ok, %File.Stat{type: type}} when type in [:directory, :symlink] ->
:ok = :hex_erl_tar.add(tar, string_to_charlist(name), [])
:ok = :hex_erl_tar.add(tar, string_to_charlist(name), @tar_opts)

_stat ->
contents = File.read!(name)
mode = File.stat!(name).mode
:ok = :hex_erl_tar.add(tar, contents, string_to_charlist(name), mode, [])
:ok = :hex_erl_tar.add(tar, contents, string_to_charlist(name), mode, @tar_opts)
end
end)
end
Expand Down
66 changes: 51 additions & 15 deletions src/hex_erl_tar.erl
Original file line number Diff line number Diff line change
Expand Up @@ -470,26 +470,56 @@ add(Reader, NameOrBin, NameInArchive, Mode, Options)

do_add(#reader{access=write}=Reader, Name, NameInArchive, Mode, Options)
when is_list(NameInArchive), is_list(Options) ->
RF = fun(F) -> file:read_link_info(F, [{time, posix}]) end,
RF = fun(F) -> apply_file_info_opts(Options, file:read_link_info(F, [{time, posix}])) end,
Opts = #add_opts{read_info=RF},
add1(Reader, Name, NameInArchive, Mode, add_opts(Options, Opts));
add1(Reader, Name, NameInArchive, Mode, add_opts(Options, Options, Opts));
do_add(#reader{access=read},_,_,_,_) ->
{error, eacces};
do_add(Reader,_,_,_,_) ->
{error, {badarg, Reader}}.

add_opts([dereference|T], Opts) ->
RF = fun(F) -> file:read_file_info(F, [{time, posix}]) end,
add_opts(T, Opts#add_opts{read_info=RF});
add_opts([verbose|T], Opts) ->
add_opts(T, Opts#add_opts{verbose=true});
add_opts([{chunks,N}|T], Opts) ->
add_opts(T, Opts#add_opts{chunk_size=N});
add_opts([_|T], Opts) ->
add_opts(T, Opts);
add_opts([], Opts) ->
add_opts([dereference|T], AllOptions, Opts) ->
RF = fun(F) -> apply_file_info_opts(AllOptions, file:read_file_info(F, [{time, posix}])) end,
add_opts(T, AllOptions, Opts#add_opts{read_info=RF});
add_opts([verbose|T], AllOptions, Opts) ->
add_opts(T, AllOptions, Opts#add_opts{verbose=true});
add_opts([{chunks,N}|T], AllOptions, Opts) ->
add_opts(T, AllOptions, Opts#add_opts{chunk_size=N});
add_opts([{atime,Value}|T], AllOptions, Opts) ->
add_opts(T, AllOptions, Opts#add_opts{atime=Value});
add_opts([{mtime,Value}|T], AllOptions, Opts) ->
add_opts(T, AllOptions, Opts#add_opts{mtime=Value});
add_opts([{ctime,Value}|T], AllOptions, Opts) ->
add_opts(T, AllOptions, Opts#add_opts{ctime=Value});
add_opts([{uid,Value}|T], AllOptions, Opts) ->
add_opts(T, AllOptions, Opts#add_opts{uid=Value});
add_opts([{gid,Value}|T], AllOptions, Opts) ->
add_opts(T, AllOptions, Opts#add_opts{gid=Value});
add_opts([_|T], AllOptions, Opts) ->
add_opts(T, AllOptions, Opts);
add_opts([], _AllOptions, Opts) ->
Opts.

apply_file_info_opts(Opts, {ok, FileInfo}) ->
{ok, do_apply_file_info_opts(Opts, FileInfo)};
apply_file_info_opts(_Opts, Other) ->
Other.

do_apply_file_info_opts([{atime,Value}|T], FileInfo) ->
do_apply_file_info_opts(T, FileInfo#file_info{atime=Value});
do_apply_file_info_opts([{mtime,Value}|T], FileInfo) ->
do_apply_file_info_opts(T, FileInfo#file_info{mtime=Value});
do_apply_file_info_opts([{ctime,Value}|T], FileInfo) ->
do_apply_file_info_opts(T, FileInfo#file_info{ctime=Value});
do_apply_file_info_opts([{uid,Value}|T], FileInfo) ->
do_apply_file_info_opts(T, FileInfo#file_info{uid=Value});
do_apply_file_info_opts([{gid,Value}|T], FileInfo) ->
do_apply_file_info_opts(T, FileInfo#file_info{gid=Value});
do_apply_file_info_opts([_|T], FileInfo) ->
do_apply_file_info_opts(T, FileInfo);
do_apply_file_info_opts([], FileInfo) ->
FileInfo.

add1(#reader{}=Reader, Name, NameInArchive, undefined, #add_opts{read_info=ReadInfo}=Opts)
when is_list(Name) ->
Res = case ReadInfo(Name) of
Expand Down Expand Up @@ -523,13 +553,16 @@ add1(#reader{}=Reader, Name, NameInArchive, undefined, #add_opts{read_info=ReadI
end;
add1(Reader, Bin, NameInArchive, Mode, Opts) when is_binary(Bin) ->
add_verbose(Opts, "a ~ts~n", [NameInArchive]),
Now = 0,
Header = #tar_header{
name = NameInArchive,
size = byte_size(Bin),
typeflag = ?TYPE_REGULAR,
atime = 0,
mtime = 0,
ctime = 0,
atime = add_opts_time(Opts#add_opts.atime, Now),
mtime = add_opts_time(Opts#add_opts.mtime, Now),
ctime = add_opts_time(Opts#add_opts.ctime, Now),
uid = Opts#add_opts.uid,
gid = Opts#add_opts.gid,
mode = default_mode(Mode, 8#100644)},
{ok, Reader2} = add_header(Reader, Header, Opts),
Padding = skip_padding(byte_size(Bin)),
Expand All @@ -539,6 +572,9 @@ add1(Reader, Bin, NameInArchive, Mode, Opts) when is_binary(Bin) ->
{error, Reason} -> {error, {NameInArchive, Reason}}
end.

add_opts_time(undefined, _Now) -> 0;
add_opts_time(Time, _Now) -> Time.

default_mode(undefined, Mode) -> Mode;
default_mode(Mode, _) -> Mode.

Expand Down
11 changes: 8 additions & 3 deletions src/hex_erl_tar.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@

%% Options used when adding files to a tar archive.
-record(add_opts, {
read_info, %% Fun to use for read file/link info.
chunk_size = 0, %% For file reading when sending to sftp. 0=do not chunk
verbose = false}). %% Verbose on/off.
read_info, %% Fun to use for read file/link info.
chunk_size = 0, %% For file reading when sending to sftp. 0=do not chunk
verbose = false, %% Verbose on/off.
atime = undefined,
mtime = undefined,
ctime = undefined,
uid = 0,
gid = 0}).
-type add_opts() :: #add_opts{}.

%% Options used when reading a tar archive.
Expand Down

0 comments on commit afe4762

Please sign in to comment.