aboutsummaryrefslogblamecommitdiffstats
path: root/lib/ssh/test/ssh_sftp_SUITE.erl
blob: 559fa721fdc057d578d9e2c361271184e6596e76 (plain) (tree)
1
2
3
4


                   
                                                        




















                                                                         
                                           




                                        


                          

                                            
                                                                      
                                                                      
                                                                      





                                  


                               

 
                         
                                  

                        
                   

                                            
        
 
                        
               
                  


                                                                      

                                                                               
                                                                                                 
                                                                          


                                                                                     
                                                                                
                                                                                                  
                                                                           





                                                                                                   
     
 


                                        
                               


                                                  
                                                    
                                                                                       






                                                                                      
                                                                                       
                                  
                                                                              

                                        



















                                                                     





                                       
                                                                      
 










                                                        













                                                                





                                                    
                                        
 



































                                                                                     
        
 

                                                       








                                                     

                                               
                          

                                                                      
                                                                      
                                                                      

                                                     










                                                         
                                                         


                                                     
                                        

                                                                      

                                                        






                                                   
                                                   
 
                                                                      

                                            


                                                  
                                      
                                                    
                                          
 
                                                                      

                                           



                                                   
                                           

                                                                      

                                              


                                                  


                                       
                                                
                                          

                                                                      











                                                                      

                                         


                                                  


                                                   
                                                            
                                         
                                                      
                                                                
                                                 
                                                                      

                                               





                                                     
                                                   
                                                           

                                                                
                                                      
                                                      
                                                              

                                                                
                                                                  

                                                                      

                                                       


                                         
 
                                             

                                          
                                                   
                                                     
                                                 

                                                                      

                                                
                                     
                     







                                                                   
                                                                     
                                                                   


                                                                      

                                                  


                                                   
 
                                      
                                                             


                                                                  
                                                             

                                                                      

                                                  


                                                  
 
                                      

                                         


                                                                          
                                                  


                                                                      

                               


                                          
 





                                                         
                                         

               

                        
                                                                      

                                











                                                          

                        


                                                                      

                                             






                                                  

















                                                         
                                               

                                                                      

                                                      







                                                         







                                                              
                        



                       
                                                               
 
                                                                      

                                                        
                                         














                                                                     
                        




                                                                 
                                                        
 
                                                                      
                               
                                                                                  








                                                                      



                                              
















































                                                                                
                                                                      
                                                                      









                                                                      
                                            








                                                                            










































                                                                                 
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2005-2014. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%

%%
-module(ssh_sftp_SUITE).

%% Note: This directive should only be used in test suites.
-compile(export_all).

-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").

% Default timetrap timeout
-define(default_timeout, ?t:minutes(1)).

-define(USER, "Alladin").
-define(PASSWD, "Sesame").

-define(tar_file_name, "sftp_tar_test.tar").

%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------

suite() ->
    [{ct_hooks,[ts_install_cth]}].

all() -> 
    [{group, erlang_server},
     {group, openssh_server},
     sftp_nonexistent_subsystem
    ].


init_per_suite(Config) ->
    case (catch crypto:start()) of
	ok ->
	    ssh:start(),
	    Config;
	_ ->
	    {skip,"Could not start crypto!"}
    end.

end_per_suite(Config) ->
    ssh:stop(),
    crypto:stop(),
    Config.

%%--------------------------------------------------------------------
groups() -> 
    [{erlang_server, [], [open_close_file, open_close_dir, read_file, read_dir,
			  write_file, write_big_file, rename_file, mk_rm_dir, remove_file, links,
			  retrieve_attributes, set_attributes, async_read,
			  async_write, position, pos_read, pos_write, version_option,
			  {group,remote_tar_write}
			 ]},
     {openssh_server, [], [open_close_file, open_close_dir, read_file, read_dir,
			   write_file, write_big_file, rename_file, mk_rm_dir, remove_file, links,
			   retrieve_attributes, set_attributes, async_read,
			   async_write, position, pos_read, pos_write,
			   {group,remote_tar_write}]},

     {remote_tar_write, [], [create_empty_tar, files_to_tar, big_file_to_tar, files_chunked_to_tar,
			     directory_to_tar, binaries_to_tar]}
    ].
     

init_per_group(erlang_server, Config) ->
    PrivDir = ?config(priv_dir, Config),
    SysDir =  ?config(data_dir, Config),
    Sftpd = {_, HostX, PortX} =
	ssh_test_lib:daemon([{system_dir, SysDir},
			     {user_dir, PrivDir},
			     {user_passwords,
			      [{?USER, ?PASSWD}]}]),
    [{peer, {fmt_host(HostX),PortX}}, {group, erlang_server}, {sftpd, Sftpd} | Config];

init_per_group(openssh_server, Config) ->
    Host = ssh_test_lib:hostname(),
    case (catch ssh_sftp:start_channel(Host,					      
				       [{user_interaction, false},
					{silently_accept_hosts, true}])) of
	{ok, _ChannelPid, Connection} ->
	    [{peer, {_HostName,{IPx,Portx}}}] = ssh:connection_info(Connection,[peer]),
	    ssh:close(Connection),
	    [{peer, {fmt_host(IPx),Portx}}, {group, openssh_server} | Config];
	_ ->
	    {skip, "No openssh server"} 
    end;

init_per_group(remote_tar_write, Config) ->
    {Host,Port} =  ?config(peer, Config),
    ct:log("Server (~p) at ~p:~p",[?config(group,Config),Host,Port]),
    {ok, Connection} =
	case ?config(group, Config) of
	    erlang_server ->
		ssh:connect(Host, Port,
			    [{user, ?USER},
			     {password, ?PASSWD},
			     {user_interaction, false},
			     {silently_accept_hosts, true}]);
	    openssh_server ->
		ssh:connect(Host, Port,
			    [{user_interaction, false},
			     {silently_accept_hosts, true}])
	end,
    [{remote_tar_write, true}, 
     {connection, Connection} | Config].

end_per_group(erlang_server, Config) ->
    Config;
end_per_group(_, Config) ->
    Config.

%%--------------------------------------------------------------------

init_per_testcase(sftp_nonexistent_subsystem, Config) ->
    PrivDir = ?config(priv_dir, Config),
    SysDir =  ?config(data_dir, Config),
    Sftpd = ssh_test_lib:daemon([{system_dir, SysDir},
				 {user_dir, PrivDir},
				 {subsystems, []},
				 {user_passwords,
				  [{?USER, ?PASSWD}]}
				]),
    [{sftpd, Sftpd} | Config];

init_per_testcase(version_option, Config) ->
    prep(Config),
    TmpConfig0 = lists:keydelete(watchdog, 1, Config),
    TmpConfig = lists:keydelete(sftp, 1, TmpConfig0),
    Dog = ct:timetrap(?default_timeout),
    {_,Host, Port} =  ?config(sftpd, Config),
    {ok, ChannelPid, Connection}  = 
	ssh_sftp:start_channel(Host, Port,
			       [{sftp_vsn, 3},
				{user, ?USER},
				{password, ?PASSWD},
				{user_interaction, false},
				{silently_accept_hosts, true}]),
    Sftp = {ChannelPid, Connection},
    [{sftp,Sftp}, {watchdog, Dog} | TmpConfig];

init_per_testcase(Case, Config0) ->
    prep(Config0),
    Config1 = lists:keydelete(watchdog, 1, Config0),
    Config2 = lists:keydelete(sftp, 1, Config1),
    Dog = ct:timetrap(?default_timeout),

    Config =
	case ?config(group,Config2) of
	    erlang_server ->
		{_,Host, Port} =  ?config(sftpd, Config2),
		{ok, ChannelPid, Connection}  = 
		    ssh_sftp:start_channel(Host, Port,
					   [{user, ?USER},
					    {password, ?PASSWD},
					    {user_interaction, false},
					    {silently_accept_hosts, true}]),
		Sftp = {ChannelPid, Connection},
		[{sftp, Sftp}, {watchdog, Dog} | Config2];
	    openssh_server when Case == links ->
		{skip, "known bug in openssh"};
	    openssh_server ->
		Host = ssh_test_lib:hostname(),
		{ok, ChannelPid, Connection} = 
		    ssh_sftp:start_channel(Host, 
					   [{user_interaction, false},
					    {silently_accept_hosts, true}]), 
		Sftp = {ChannelPid, Connection},
		[{sftp, Sftp}, {watchdog, Dog} | Config2]
	end,

    case catch ?config(remote_tar_write,Config) of
	%% The 'catch' is for the case of Config={skip,...}
	true ->
	    %% Provide a tar Handle *independent* of the sftp-channel already opened!
	    %% This Handle will be closed (as well as ChannelPid2) in the testcase
	    {ok,ChannelPid2} = 
		ssh_sftp:start_channel(?config(connection,Config)),
	    {ok,Handle} =
		ssh_sftp:open_tar(ChannelPid2, fnp(?tar_file_name,Config), [write]),
	    [{handle,Handle} | Config];
	_ ->
	    Config
    end.

end_per_testcase(sftp_nonexistent_subsystem, Config) ->
    Config;
end_per_testcase(rename_file, Config) ->
    PrivDir = ?config(priv_dir, Config),
    NewFileName = filename:join(PrivDir, "test.txt"),
    file:delete(NewFileName),  
    end_per_testcase(Config);
end_per_testcase(_, Config) ->
    end_per_testcase(Config).

end_per_testcase(Config) ->
    {Sftp, Connection} = ?config(sftp, Config),
    ssh_sftp:stop_channel(Sftp),
    ssh:close(Connection).

%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
open_close_file() ->
    [{doc, "Test API functions open/3 and close/2"}].
open_close_file(Config) when is_list(Config) ->
    PrivDir =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "sftp.txt"),

    {Sftp, _} = ?config(sftp, Config),

    ok = open_close_file(Sftp, FileName, [read]),
    ok = open_close_file(Sftp, FileName, [write]),
    ok = open_close_file(Sftp, FileName, [write, creat]),
    ok = open_close_file(Sftp, FileName, [write, trunc]),
    ok = open_close_file(Sftp, FileName, [append]),
    ok = open_close_file(Sftp, FileName, [read, binary]).

open_close_file(Server, File, Mode) ->
    {ok, Handle} = ssh_sftp:open(Server, File, Mode),
    ok = ssh_sftp:close(Server, Handle).

%%--------------------------------------------------------------------
open_close_dir() ->
    [{doc, "Test API functions opendir/2 and close/2"}].
open_close_dir(Config) when is_list(Config) ->
    PrivDir = ?config(priv_dir, Config),
    {Sftp, _} = ?config(sftp, Config),
    FileName = filename:join(PrivDir, "sftp.txt"),

    {ok, Handle} = ssh_sftp:opendir(Sftp, PrivDir),
    ok = ssh_sftp:close(Sftp, Handle),
    {error, _} =  ssh_sftp:opendir(Sftp, FileName).

%%--------------------------------------------------------------------
read_file() ->
    [{doc, "Test API funtion read_file/2"}].
read_file(Config) when is_list(Config) ->
    PrivDir =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "sftp.txt"),
    {Sftp, _} = ?config(sftp, Config),
    {ok, Data} = ssh_sftp:read_file(Sftp, FileName),
    {ok, Data} = file:read_file(FileName).

%%--------------------------------------------------------------------
read_dir() ->
    [{doc,"Test API function list_dir/2"}].
read_dir(Config) when is_list(Config) ->
    PrivDir = ?config(priv_dir, Config),
    {Sftp, _} = ?config(sftp, Config),
    {ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir),
    ct:pal("sftp list dir: ~p~n", [Files]).

%%--------------------------------------------------------------------
write_file() ->
    [{doc, "Test API function write_file/2"}].
write_file(Config) when is_list(Config) ->
    PrivDir =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "sftp.txt"),
    {Sftp, _} = ?config(sftp, Config),

    Data = list_to_binary("Hej hopp!"),
    ssh_sftp:write_file(Sftp, FileName, [Data]),
    {ok, Data} = file:read_file(FileName).

%%--------------------------------------------------------------------
write_big_file() ->
    [{doc, "Test API function write_file/2 with big data"}].
write_big_file(Config) when is_list(Config) ->
    PrivDir =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "sftp.txt"),
    {Sftp, _} = ?config(sftp, Config),

    Data = list_to_binary(lists:duplicate(750000,"a")),
    ssh_sftp:write_file(Sftp, FileName, [Data]),
    {ok, Data} = file:read_file(FileName).

%%--------------------------------------------------------------------
remove_file() ->
    [{doc,"Test API function delete/2"}].
remove_file(Config) when is_list(Config) ->
    PrivDir =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "sftp.txt"),
    {Sftp, _} = ?config(sftp, Config),

    {ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir),
    true = lists:member(filename:basename(FileName), Files),
    ok = ssh_sftp:delete(Sftp, FileName),
    {ok, NewFiles} = ssh_sftp:list_dir(Sftp, PrivDir),
    false = lists:member(filename:basename(FileName), NewFiles),
    {error, _} = ssh_sftp:delete(Sftp, FileName).
%%--------------------------------------------------------------------
rename_file() ->
    [{doc, "Test API function rename_file/2"}].
rename_file(Config) when is_list(Config) ->
    PrivDir =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "sftp.txt"),
    NewFileName = filename:join(PrivDir, "test.txt"),

    {Sftp, _} = ?config(sftp, Config),
    {ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir),
    ct:pal("FileName: ~p, Files: ~p~n", [FileName, Files]),
    true = lists:member(filename:basename(FileName), Files),
    false = lists:member(filename:basename(NewFileName), Files),
    ok = ssh_sftp:rename(Sftp, FileName, NewFileName),
    {ok, NewFiles} = ssh_sftp:list_dir(Sftp, PrivDir),
    ct:pal("FileName: ~p, Files: ~p~n", [FileName, NewFiles]),

    false = lists:member(filename:basename(FileName), NewFiles),
    true = lists:member(filename:basename(NewFileName), NewFiles).

%%--------------------------------------------------------------------
mk_rm_dir() ->
    [{doc,"Test API functions make_dir/2, del_dir/2"}].
mk_rm_dir(Config) when is_list(Config) ->
    PrivDir = ?config(priv_dir, Config),
    {Sftp, _} = ?config(sftp, Config),
 
    DirName = filename:join(PrivDir, "test"),
    ok = ssh_sftp:make_dir(Sftp, DirName),
    ok = ssh_sftp:del_dir(Sftp, DirName),
    NewDirName = filename:join(PrivDir, "foo/bar"),
    {error, _} = ssh_sftp:make_dir(Sftp, NewDirName),
    {error, _} = ssh_sftp:del_dir(Sftp, PrivDir).

%%--------------------------------------------------------------------
links() ->
    [{doc,"Tests API function make_symlink/3"}].
links(Config) when is_list(Config) ->
    case os:type() of
	{win32, _} ->
	    {skip, "Links are not fully supported by windows"};
	_ ->
	    {Sftp, _} = ?config(sftp, Config),
	    PrivDir =  ?config(priv_dir, Config),
	    FileName = filename:join(PrivDir, "sftp.txt"),
	    LinkFileName = filename:join(PrivDir, "link_test.txt"),

	    ok = ssh_sftp:make_symlink(Sftp, LinkFileName, FileName),
	    {ok, FileName} = ssh_sftp:read_link(Sftp, LinkFileName)
    end.

%%--------------------------------------------------------------------
retrieve_attributes() ->
    [{doc, "Test API function read_file_info/3"}].
retrieve_attributes(Config) when is_list(Config) ->
    PrivDir =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "sftp.txt"),

    {Sftp, _} = ?config(sftp, Config),
    {ok, FileInfo} = ssh_sftp:read_file_info(Sftp, FileName),
    {ok, NewFileInfo} = file:read_file_info(FileName),

    %% TODO comparison. There are some differences now is that ok?
    ct:pal("SFTP: ~p   FILE: ~p~n", [FileInfo, NewFileInfo]).

%%--------------------------------------------------------------------
set_attributes() ->
    [{doc,"Test API function write_file_info/3"}].
set_attributes(Config) when is_list(Config) ->
    PrivDir =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "test.txt"),

    {Sftp, _} = ?config(sftp, Config),
    {ok,Fd} = file:open(FileName, write),
    io:put_chars(Fd,"foo"),
    ok = ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#400}),
    {error, eacces} = file:write_file(FileName, "hello again"),
    ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#600}),
    ok = file:write_file(FileName, "hello again").

%%--------------------------------------------------------------------

async_read() ->
    [{doc,"Test API aread/3"}].
async_read(Config) when is_list(Config) ->
    {Sftp, _} = ?config(sftp, Config),
    PrivDir =  ?config(priv_dir, Config),

    FileName = filename:join(PrivDir, "sftp.txt"),
    {ok, Handle} = ssh_sftp:open(Sftp, FileName, [read]),
    {async, Ref} = ssh_sftp:aread(Sftp, Handle, 20),

    receive
	{async_reply, Ref, {ok, Data}} ->
	    ct:pal("Data: ~p~n", [Data]),
	    ok;
	Msg ->
	    ct:fail(Msg)
    end.
%%--------------------------------------------------------------------
async_write() ->
    [{doc,"Test API awrite/3"}].
async_write(Config) when is_list(Config) ->
    {Sftp, _} = ?config(sftp, Config),
    PrivDir =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "test.txt"),
    {ok, Handle} = ssh_sftp:open(Sftp, FileName, [write]),
    Data = list_to_binary("foobar"),
    {async, Ref} = ssh_sftp:awrite(Sftp, Handle, Data),

    receive
	{async_reply, Ref, ok} ->
	    {ok, Data} = file:read_file(FileName);
	Msg ->
	    ct:fail(Msg)
    end.

%%--------------------------------------------------------------------

position() ->
    [{doc, "Test API functions position/3"}].
position(Config) when is_list(Config) ->
    PrivDir =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "test.txt"),
    {Sftp, _} = ?config(sftp, Config),

    Data = list_to_binary("1234567890"),
    ssh_sftp:write_file(Sftp, FileName, [Data]),
    {ok, Handle} = ssh_sftp:open(Sftp, FileName, [read]),

    {ok, 3} = ssh_sftp:position(Sftp, Handle, {bof, 3}),
    {ok, "4"} = ssh_sftp:read(Sftp, Handle, 1),

    {ok, 10} = ssh_sftp:position(Sftp, Handle, eof),
    eof = ssh_sftp:read(Sftp, Handle, 1),

    {ok, 6} = ssh_sftp:position(Sftp, Handle, {bof, 6}),
    {ok, "7"} = ssh_sftp:read(Sftp, Handle, 1),

    {ok, 9} = ssh_sftp:position(Sftp, Handle, {cur, 2}),
    {ok, "0"} = ssh_sftp:read(Sftp, Handle, 1),

    {ok, 0} = ssh_sftp:position(Sftp, Handle, bof),
    {ok, "1"} = ssh_sftp:read(Sftp, Handle, 1),

    {ok, 1} = ssh_sftp:position(Sftp, Handle, cur),
    {ok, "2"} = ssh_sftp:read(Sftp, Handle, 1).

%%--------------------------------------------------------------------
pos_read() ->
    [{doc,"Test API functions pread/3 and apread/3"}].
pos_read(Config) when is_list(Config) ->
    PrivDir =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "test.txt"),
    {Sftp, _} = ?config(sftp, Config),
    Data = list_to_binary("Hej hopp!"),
    ssh_sftp:write_file(Sftp, FileName, [Data]),

    {ok, Handle} = ssh_sftp:open(Sftp, FileName, [read]),
    {async, Ref} = ssh_sftp:apread(Sftp, Handle, {bof, 5}, 4),

    NewData  = "opp!",

    receive
	{async_reply, Ref, {ok, NewData}} ->
	    ok;
	Msg ->
	    ct:fail(Msg)
    end,

    NewData1  = "hopp",

    {ok, NewData1} = ssh_sftp:pread(Sftp, Handle, {bof, 4}, 4).

%%--------------------------------------------------------------------
pos_write() ->
    [{doc,"Test API functions pwrite/4 and apwrite/4"}].
pos_write(Config) when is_list(Config) ->
    PrivDir =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "test.txt"),
    {Sftp, _} = ?config(sftp, Config),

    {ok, Handle} = ssh_sftp:open(Sftp, FileName, [write]),

    Data = list_to_binary("Bye,"),
    ssh_sftp:write_file(Sftp, FileName, [Data]),

    NewData = list_to_binary(" see you tomorrow"),
    {async, Ref} = ssh_sftp:apwrite(Sftp, Handle, {bof, 4}, NewData),
    receive
	{async_reply, Ref, ok} ->
	    ok;
	Msg ->
	    ct:fail(Msg)
    end,

    ok = ssh_sftp:pwrite(Sftp, Handle, eof, list_to_binary("!")),

    NewData1 = list_to_binary("Bye, see you tomorrow!"),
    {ok, NewData1} = ssh_sftp:read_file(Sftp, FileName).

%%--------------------------------------------------------------------
sftp_nonexistent_subsystem() ->
    [{doc, "Try to execute sftp subsystem on a server that does not support it"}].
sftp_nonexistent_subsystem(Config) when is_list(Config) ->
    {_,Host, Port} =  ?config(sftpd, Config),
    {error,"server failed to start sftp subsystem"} =
	ssh_sftp:start_channel(Host, Port,
			       [{user_interaction, false},
				{user, ?USER}, {password, ?PASSWD},
				{silently_accept_hosts, true}]).

%%--------------------------------------------------------------------
version_option()  ->
    [{doc, "Test API option sftp_vsn"}].
version_option(Config) when is_list(Config) ->
    open_close_dir(Config).

%%--------------------------------------------------------------------
create_empty_tar(Config) ->
    {ChPid,_} = ?config(sftp,Config),
    Handle = ?config(handle,Config),
    erl_tar:close(Handle),
    {ok, #file_info{type=regular}} =
	ssh_sftp:read_file_info(ChPid,fnp(?tar_file_name,Config)).
    
%%--------------------------------------------------------------------
files_to_tar(Config) ->
    Handle = ?config(handle,Config),
    ok = erl_tar:add(Handle, fn("f1.txt",Config), "f1.txt", []),
    ok = erl_tar:add(Handle, fn("f2.txt",Config), "f2.txt", []),
    ok = erl_tar:close(Handle),
    chk_tar(["f1.txt", "f2.txt"], Config).


%%--------------------------------------------------------------------
big_file_to_tar(Config) ->
    Handle = ?config(handle,Config),
    ok = erl_tar:add(Handle, fn("big.txt",Config), "big.txt", []),
    ok = erl_tar:close(Handle),
    chk_tar(["big.txt"], Config).


%%--------------------------------------------------------------------
files_chunked_to_tar(Config) ->
    Handle = ?config(handle,Config),
    ok = erl_tar:add(Handle, fn("f1.txt",Config), "f1.txt", [{chunks,2}]),
    ok = erl_tar:add(Handle, fn("big.txt",Config), "big.txt", [{chunks,15000}]),
    ok = erl_tar:close(Handle),
    chk_tar(["f1.txt", "big.txt"], Config).

%%--------------------------------------------------------------------
directory_to_tar(Config) ->
    Handle = ?config(handle,Config),
    ok = erl_tar:add(Handle, fn("d1",Config), "d1", []),
    ok = erl_tar:close(Handle),
    chk_tar(["d1/f1", "d1/f2"], Config).
    
%%--------------------------------------------------------------------
binaries_to_tar(Config) ->
    Handle = ?config(handle,Config),
    Bin = <<"A binary">>,
    ok = erl_tar:add(Handle, Bin, "b1", []),
    ok = erl_tar:close(Handle),
    chk_tar([{"b1",Bin}], Config).

%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
prep(Config) ->
    PrivDir =  ?config(priv_dir, Config),
    TestFile = filename:join(PrivDir, "sftp.txt"),
    TestFile1 = filename:join(PrivDir, "test.txt"),
    TestLink = filename:join(PrivDir, "link_test.txt"),

    file:delete(TestFile),
    file:delete(TestFile1),
    file:delete(TestLink),
    file:delete(fnp(?tar_file_name,Config)),

    %% Initial config
    DataDir = ?config(data_dir, Config),
    FileName = filename:join(DataDir, "sftp.txt"),
    file:copy(FileName, TestFile),
    Mode = 8#00400 bor 8#00200 bor 8#00040, % read & write owner, read group
    {ok, FileInfo} = file:read_file_info(TestFile),
    ok = file:write_file_info(TestFile,
			      FileInfo#file_info{mode = Mode}).


chk_tar(Items, Config) ->
    %% FIXME: ought to check that no more than expected is present...
    {ChPid,_} = ?config(sftp,Config),
    ok = file:set_cwd(?config(priv_dir,Config)),
    file:make_dir("tar_chk"), % May already exist
    ok = file:set_cwd("tar_chk"),
    {ok,Data} = ssh_sftp:read_file(ChPid, fnp(?tar_file_name,Config)),
    ok = file:write_file(?tar_file_name, Data),
    os:cmd("tar xf "++?tar_file_name),
    lists:foreach(fun(Item) -> chk_contents(Item,Config) end,
		  Items).

	      
chk_contents({Name,ExpectBin}, _Config) -> 
    case file:read_file(Name) of
	{ok,ExpectBin} ->
	    ok;
	{ok,OtherBin} ->
	    ct:log("File: ~p~n   Got: ~p~nExpect: ~p",[Name,OtherBin,ExpectBin]),
	    ct:fail("Bad contents in file ~p",[Name]);
	Other ->
	    ct:log("File: ~p~nOther: ~p",[Name,Other]),
	    ct:fail("Error reading of file ~p",[Name])
    end;
chk_contents(Name, Config) -> 
    {ok,Bin} = file:read_file(fn(Name,Config)),
    chk_contents({Name,Bin}, Config).


fn(Name, Config) ->
    Dir = ?config(data_dir, Config),
    filename:join([Dir,"sftp_tar_test_data",Name]).

fnp(Name, Config) ->
    Dir = ?config(priv_dir, Config),
    filename:join([Dir,Name]).


fmt_host({A,B,C,D}) -> lists:concat([A,".",B,".",C,".",D]);
fmt_host(S) -> S.