diff options
author | Sverker Eriksson <[email protected]> | 2017-08-30 20:55:08 +0200 |
---|---|---|
committer | Sverker Eriksson <[email protected]> | 2017-08-30 20:55:08 +0200 |
commit | 7c67bbddb53c364086f66260701bc54a61c9659c (patch) | |
tree | 92ab0d4b91d5e2f6e7a3f9d61ea25089e8a71fe0 /lib/ssh/test/ssh_sftp_SUITE.erl | |
parent | 97dc5e7f396129222419811c173edc7fa767b0f8 (diff) | |
parent | 3b7a6ffddc819bf305353a593904cea9e932e7dc (diff) | |
download | otp-7c67bbddb53c364086f66260701bc54a61c9659c.tar.gz otp-7c67bbddb53c364086f66260701bc54a61c9659c.tar.bz2 otp-7c67bbddb53c364086f66260701bc54a61c9659c.zip |
Merge tag 'OTP-19.0' into sverker/19/binary_to_atom-utf8-crash/ERL-474/OTP-14590
Diffstat (limited to 'lib/ssh/test/ssh_sftp_SUITE.erl')
-rw-r--r-- | lib/ssh/test/ssh_sftp_SUITE.erl | 965 |
1 files changed, 817 insertions, 148 deletions
diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl index 56b1363b7a..19ad81e7da 100644 --- a/lib/ssh/test/ssh_sftp_SUITE.erl +++ b/lib/ssh/test/ssh_sftp_SUITE.erl @@ -1,18 +1,19 @@ -%% +% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2013. All Rights Reserved. +%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at %% -%% 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/. +%% http://www.apache.org/licenses/LICENSE-2.0 %% -%% 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. +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% @@ -25,135 +26,287 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). - -% Default timetrap timeout +-include("ssh_test_lib.hrl"). + % Default timetrap timeout -define(default_timeout, ?t:minutes(1)). --define(USER, "Alladin"). --define(PASSWD, "Sesame"). - %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- suite() -> - [{ct_hooks,[ts_install_cth]}]. + [{ct_hooks,[ts_install_cth]}, + {timetrap,{seconds,40}}]. all() -> - [{group, erlang_server}, - {group, openssh_server}, - sftp_nonexistent_subsystem + [{group, not_unicode}, + {group, unicode} ]. init_per_suite(Config) -> - case (catch crypto:start()) of - ok -> - ssh:start(), - Config; - _ -> - {skip,"Could not start crypto!"} - end. + ?CHECK_CRYPTO( + begin + ct:log("file:native_name_encoding() = ~p,~nio:getopts() = ~p", + [file:native_name_encoding(),io:getopts()]), + ssh:start(), + Config + end). -end_per_suite(Config) -> - ssh:stop(), - crypto:stop(), - Config. +end_per_suite(_onfig) -> + ssh:stop(). %%-------------------------------------------------------------------- groups() -> - [{erlang_server, [], [open_close_file, open_close_dir, read_file, read_dir, - write_file, rename_file, mk_rm_dir, remove_file, links, - retrieve_attributes, set_attributes, async_read, - async_write, position, pos_read, pos_write]}, - {openssh_server, [], [open_close_file, open_close_dir, read_file, read_dir, - write_file, rename_file, mk_rm_dir, remove_file, links, - retrieve_attributes, set_attributes, async_read, - async_write, position, pos_read, pos_write]}]. - + [{not_unicode, [], [{group,erlang_server}, + {group,openssh_server}, + sftp_nonexistent_subsystem]}, + + {unicode, [], [{group,erlang_server}, + {group,openssh_server}, + sftp_nonexistent_subsystem]}, + + {erlang_server, [], [{group,write_read_tests}, + version_option, + {group,remote_tar}]}, + + {openssh_server, [], [{group,write_read_tests}, + {group,remote_tar}]}, + + {remote_tar, [], [create_empty_tar, + ascii_filename_ascii_contents_to_tar, + ascii_filename_unicode_contents_to_tar, + unicode_filename_ascii_contents_to_tar, + files_to_tar, + big_file_to_tar, files_chunked_to_tar, + directory_to_tar, binaries_to_tar, null_crypto_tar, + simple_crypto_tar_small, simple_crypto_tar_big, + read_tar, read_null_crypto_tar, read_crypto_tar, + aes_cbc256_crypto_tar, aes_ctr_stream_crypto_tar + ]}, + + {write_read_tests, [], [open_close_file, open_close_dir, read_file, read_dir, + write_file, write_file_iolist, write_big_file, sftp_read_big_file, + rename_file, mk_rm_dir, remove_file, links, + retrieve_attributes, set_attributes, async_read, + async_write, position, pos_read, pos_write, + start_channel_sock + ]} + ]. + +init_per_group(not_unicode, Config) -> + ct:comment("Begin ~p",[grps(Config)]), + DataDir = proplists:get_value(data_dir, Config), + [{user, "Alladin"}, + {passwd, "Sesame"}, + {data, <<"Hello world!">>}, + {filename, "sftp.txt"}, + {testfile, "test.txt"}, + {linktest, "link_test.txt"}, + {tar_filename, "sftp_tar_test.tar"}, + {tar_F1_txt, "f1.txt"}, + {datadir_tar, filename:join(DataDir,"sftp_tar_test_data")} + | Config]; + +init_per_group(unicode, Config) -> + case (file:native_name_encoding() == utf8) + andalso ("四" == [22235]) + of + true -> + ct:comment("Begin ~p",[grps(Config)]), + DataDir = proplists:get_value(data_dir, Config), + NewConfig = + [{user, "åke高兴"}, + {passwd, "ärlig日本じん"}, + {data, <<"foobar å 一二三四いちにさんち">>}, + {filename, "sftp瑞点.txt"}, + {testfile, "testハンス.txt"}, + {linktest, "link_test語.txt"}, + {tar_filename, "sftp_tar_test一二三.tar"}, + {tar_F1_txt, "F一.txt"}, + {tar_F3_txt, "f3.txt"}, + {tar_F4_txt, "g四.txt"}, + {datadir_tar, filename:join(DataDir,"sftp_tar_test_data_高兴")} + | lists:foldl(fun(K,Cf) -> lists:keydelete(K,1,Cf) end, + Config, + [user, passwd, data, + filename, testfile, linktest, + tar_filename, tar_F1_txt, datadir_tar + ] + ) + ], + FN = fn(proplists:get_value(tar_F1_txt,NewConfig), NewConfig), + case catch file:read_file(FN) of + {ok,FN_contents} -> + ct:log("Readable file:read_file(~tp) ->~n~tp",[FN,FN_contents]), + NewConfig; + Other -> + ct:log("Unreadable file:read_file(~tp) ->~n~p",[FN,Other]), + {skip, "Not unicode file reading"} + end; + + _ -> + {skip, "Not unicode file encoding"} + end; + init_per_group(erlang_server, Config) -> - PrivDir = ?config(priv_dir, Config), - SysDir = ?config(data_dir, Config), - Sftpd = + ct:comment("Begin ~p",[grps(Config)]), + PrivDir = proplists:get_value(priv_dir, Config), + SysDir = proplists:get_value(data_dir, Config), + User = proplists:get_value(user, Config), + Passwd = proplists:get_value(passwd, Config), + Sftpd = {_, HostX, PortX} = ssh_test_lib:daemon([{system_dir, SysDir}, {user_dir, PrivDir}, {user_passwords, - [{?USER, ?PASSWD}]}]), - [{group, erlang_server}, {sftpd, Sftpd} | Config]; + [{User, Passwd}]}]), + [{peer, {fmt_host(HostX),PortX}}, {group, erlang_server}, {sftpd, Sftpd} | Config]; init_per_group(openssh_server, Config) -> + ct:comment("Begin ~p",[grps(Config)]), Host = ssh_test_lib:hostname(), - case (catch ssh_sftp:start_channel(Host, + 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), - [{group, openssh_server} | Config]; + [{peer, {fmt_host(IPx),Portx}}, {group, openssh_server} | Config]; + {error,"Key exchange failed"} -> + {skip, "openssh server doesn't support the tested kex algorithm"}; _ -> {skip, "No openssh server"} - end. + end; + +init_per_group(remote_tar, Config) -> + ct:comment("Begin ~p",[grps(Config)]), + {Host,Port} = proplists:get_value(peer, Config), + ct:log("Server (~p) at ~p:~p",[proplists:get_value(group,Config),Host,Port]), + User = proplists:get_value(user, Config), + Passwd = proplists:get_value(passwd, Config), + {ok, Connection} = + case proplists:get_value(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, true}, + {connection, Connection} | Config]; + +init_per_group(write_read_tests, Config) -> + ct:comment("Begin ~p",[grps(Config)]), + Config. + +grps(Config) -> + proplists:get_all_values( + name, + lists:flatten([proplists:get_value(tc_group_properties,Config,[]), + proplists:get_value(tc_group_path,Config,[])])). end_per_group(erlang_server, Config) -> + ct:comment("End ~p",[grps(Config)]), Config; end_per_group(_, Config) -> + ct:comment("End ~p",[grps(Config)]), Config. %%-------------------------------------------------------------------- init_per_testcase(sftp_nonexistent_subsystem, Config) -> - PrivDir = ?config(priv_dir, Config), - SysDir = ?config(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), + SysDir = proplists:get_value(data_dir, Config), + User = proplists:get_value(user, Config), + Passwd = proplists:get_value(passwd, Config), Sftpd = ssh_test_lib:daemon([{system_dir, SysDir}, {user_dir, PrivDir}, {subsystems, []}, {user_passwords, - [{?USER, ?PASSWD}]} + [{User, Passwd}]} ]), [{sftpd, Sftpd} | Config]; -init_per_testcase(Case, Config) -> - prep(Config), +init_per_testcase(version_option, Config0) -> + Config = prepare(Config0), TmpConfig0 = lists:keydelete(watchdog, 1, Config), TmpConfig = lists:keydelete(sftp, 1, TmpConfig0), Dog = ct:timetrap(?default_timeout), - - case ?config(group, Config) of - erlang_server -> - {_,Host, Port} = ?config(sftpd, Config), - {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} | TmpConfig]; - 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} | TmpConfig] + {_,Host, Port} = proplists:get_value(sftpd, Config), + User = proplists:get_value(user, Config), + Passwd = proplists:get_value(passwd, 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, Config00) -> + Config0 = prepare(Config00), + Config1 = lists:keydelete(watchdog, 1, Config0), + Config2 = lists:keydelete(sftp, 1, Config1), + Dog = ct:timetrap(2 * ?default_timeout), + User = proplists:get_value(user, Config0), + Passwd = proplists:get_value(passwd, Config0), + + Config = + case proplists:get_value(group,Config2) of + erlang_server -> + {_,Host, Port} = proplists:get_value(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 proplists:get_value(remote_tar,Config) of + %% The 'catch' is for the case of Config={skip,...} + true -> + %% Provide a ChannelPid independent of the sftp-channel already opened. + {ok,ChPid2} = ssh_sftp:start_channel(proplists:get_value(connection,Config)), + [{channel_pid2,ChPid2} | 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"), + NewFileName = proplists:get_value(testfile, Config), 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). + {Sftp, Connection} = proplists:get_value(sftp, Config), + ok = ssh_sftp:stop_channel(Sftp), + catch ssh_sftp:stop_channel(proplists:get_value(channel_pid2, Config)), + ok = ssh:close(Connection). %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- @@ -161,10 +314,9 @@ end_per_testcase(Config) -> 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"), + FileName = proplists:get_value(filename, Config), - {Sftp, _} = ?config(sftp, Config), + {Sftp, _} = proplists:get_value(sftp, Config), ok = open_close_file(Sftp, FileName, [read]), ok = open_close_file(Sftp, FileName, [write]), @@ -181,9 +333,9 @@ open_close_file(Server, File, Mode) -> 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"), + PrivDir = proplists:get_value(sftp_priv_dir, Config), + {Sftp, _} = proplists:get_value(sftp, Config), + FileName = proplists:get_value(filename, Config), {ok, Handle} = ssh_sftp:opendir(Sftp, PrivDir), ok = ssh_sftp:close(Sftp, Handle), @@ -193,9 +345,9 @@ open_close_dir(Config) when is_list(Config) -> 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), + FileName = proplists:get_value(filename, Config), + {Sftp, _} = proplists:get_value(sftp, Config), + {ok, Data} = ssh_sftp:read_file(Sftp, FileName), {ok, Data} = ssh_sftp:read_file(Sftp, FileName), {ok, Data} = file:read_file(FileName). @@ -203,53 +355,96 @@ read_file(Config) when is_list(Config) -> 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), + PrivDir = proplists:get_value(sftp_priv_dir, Config), + {Sftp, _} = proplists:get_value(sftp, Config), {ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir), - ct:pal("sftp list dir: ~p~n", [Files]). + ct:log("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), + FileName = proplists:get_value(filename, Config), + {Sftp, _} = proplists:get_value(sftp, Config), Data = list_to_binary("Hej hopp!"), - ssh_sftp:write_file(Sftp, FileName, [Data]), + ok = ssh_sftp:write_file(Sftp, FileName, [Data]), {ok, Data} = file:read_file(FileName). %%-------------------------------------------------------------------- +write_file_iolist() -> + [{doc, "Test API function write_file/2 with iolists"}]. +write_file_iolist(Config) when is_list(Config) -> + FileName = proplists:get_value(filename, Config), + {Sftp, _} = proplists:get_value(sftp, Config), + + Data = list_to_binary("Hej hopp!"), + lists:foreach( + fun(D) -> + ok = ssh_sftp:write_file(Sftp, FileName, [D]), + Expected = if is_binary(D) -> D; + is_list(D) -> list_to_binary(D) + end, + {ok, Expected} = file:read_file(FileName) + end, + [Data, [Data,Data], [[Data],[Data]], [[[Data]],[[[[Data]],Data]]], + [[[[Data]],Data],binary_to_list(Data)], + [[[[Data]],Data],[[binary_to_list(Data)],[[binary_to_list(Data)]]]] + ]). + +%%-------------------------------------------------------------------- +write_big_file() -> + [{doc, "Test API function write_file/2 with big data"}]. +write_big_file(Config) when is_list(Config) -> + FileName = proplists:get_value(filename, Config), + {Sftp, _} = proplists:get_value(sftp, Config), + + Data = list_to_binary(lists:duplicate(750000,"a")), + ok = ssh_sftp:write_file(Sftp, FileName, [Data]), + {ok, Data} = file:read_file(FileName). + +%%-------------------------------------------------------------------- +sftp_read_big_file() -> + [{doc, "Test API function read_file/2 with big data"}]. +sftp_read_big_file(Config) when is_list(Config) -> + FileName = proplists:get_value(filename, Config), + {Sftp, _} = proplists:get_value(sftp, Config), + + Data = list_to_binary(lists:duplicate(750000,"a")), + ct:log("Data size to write is ~p bytes",[size(Data)]), + ok = ssh_sftp:write_file(Sftp, FileName, [Data]), + {ok, Data} = ssh_sftp:read_file(Sftp, 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), + PrivDir = proplists:get_value(sftp_priv_dir, Config), + FileName = proplists:get_value(filename, Config), + {Sftp, _} = proplists:get_value(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). + {error, no_such_file} = 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"), + PrivDir = proplists:get_value(sftp_priv_dir, Config), + FileName = proplists:get_value(filename, Config), + NewFileName = proplists:get_value(testfile, Config), - {Sftp, _} = ?config(sftp, Config), + {Sftp, _} = proplists:get_value(sftp, Config), {ok, Files} = ssh_sftp:list_dir(Sftp, PrivDir), - ct:pal("FileName: ~p, Files: ~p~n", [FileName, Files]), + ct:log("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]), + ct:log("FileName: ~p, Files: ~p~n", [FileName, NewFiles]), false = lists:member(filename:basename(FileName), NewFiles), true = lists:member(filename:basename(NewFileName), NewFiles). @@ -258,9 +453,9 @@ rename_file(Config) when is_list(Config) -> 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), - + PrivDir = proplists:get_value(sftp_priv_dir, Config), + {Sftp, _} = proplists:get_value(sftp, Config), + DirName = filename:join(PrivDir, "test"), ok = ssh_sftp:make_dir(Sftp, DirName), ok = ssh_sftp:del_dir(Sftp, DirName), @@ -276,10 +471,9 @@ links(Config) when is_list(Config) -> {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"), + {Sftp, _} = proplists:get_value(sftp, Config), + FileName = proplists:get_value(filename, Config), + LinkFileName = proplists:get_value(linktest, Config), ok = ssh_sftp:make_symlink(Sftp, LinkFileName, FileName), {ok, FileName} = ssh_sftp:read_link(Sftp, LinkFileName) @@ -289,29 +483,27 @@ links(Config) when is_list(Config) -> 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"), + FileName = proplists:get_value(filename, Config), - {Sftp, _} = ?config(sftp, Config), + {Sftp, _} = proplists:get_value(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]). + ct:log("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"), + FileName = proplists:get_value(testfile, Config), - {Sftp, _} = ?config(sftp, Config), + {Sftp, _} = proplists:get_value(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 = ssh_sftp:write_file_info(Sftp, FileName, #file_info{mode=8#600}), ok = file:write_file(FileName, "hello again"). %%-------------------------------------------------------------------- @@ -319,27 +511,27 @@ set_attributes(Config) when is_list(Config) -> async_read() -> [{doc,"Test API aread/3"}]. async_read(Config) when is_list(Config) -> - {Sftp, _} = ?config(sftp, Config), - PrivDir = ?config(priv_dir, Config), + {Sftp, _} = proplists:get_value(sftp, Config), - FileName = filename:join(PrivDir, "sftp.txt"), + FileName = proplists:get_value(filename, Config), {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]), + ct:log("Data: ~p~n", [Data]), ok; Msg -> ct:fail(Msg) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) 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"), + {Sftp, _} = proplists:get_value(sftp, Config), + FileName = proplists:get_value(testfile, Config), {ok, Handle} = ssh_sftp:open(Sftp, FileName, [write]), Data = list_to_binary("foobar"), {async, Ref} = ssh_sftp:awrite(Sftp, Handle, Data), @@ -356,12 +548,11 @@ async_write(Config) when is_list(Config) -> 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), + FileName = proplists:get_value(testfile, Config), + {Sftp, _} = proplists:get_value(sftp, Config), Data = list_to_binary("1234567890"), - ssh_sftp:write_file(Sftp, FileName, [Data]), + ok = ssh_sftp:write_file(Sftp, FileName, [Data]), {ok, Handle} = ssh_sftp:open(Sftp, FileName, [read]), {ok, 3} = ssh_sftp:position(Sftp, Handle, {bof, 3}), @@ -386,11 +577,10 @@ position(Config) when is_list(Config) -> 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), + FileName = proplists:get_value(testfile, Config), + {Sftp, _} = proplists:get_value(sftp, Config), Data = list_to_binary("Hej hopp!"), - ssh_sftp:write_file(Sftp, FileName, [Data]), + ok = 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), @@ -402,6 +592,8 @@ pos_read(Config) when is_list(Config) -> ok; Msg -> ct:fail(Msg) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, NewData1 = "hopp", @@ -412,14 +604,13 @@ pos_read(Config) when is_list(Config) -> 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), + FileName = proplists:get_value(testfile, Config), + {Sftp, _} = proplists:get_value(sftp, Config), {ok, Handle} = ssh_sftp:open(Sftp, FileName, [write]), Data = list_to_binary("Bye,"), - ssh_sftp:write_file(Sftp, FileName, [Data]), + ok = ssh_sftp:write_file(Sftp, FileName, [Data]), NewData = list_to_binary(" see you tomorrow"), {async, Ref} = ssh_sftp:apwrite(Sftp, Handle, {bof, 4}, NewData), @@ -428,6 +619,8 @@ pos_write(Config) when is_list(Config) -> ok; Msg -> ct:fail(Msg) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end, ok = ssh_sftp:pwrite(Sftp, Handle, eof, list_to_binary("!")), @@ -436,34 +629,510 @@ pos_write(Config) when is_list(Config) -> {ok, NewData1} = ssh_sftp:read_file(Sftp, FileName). %%-------------------------------------------------------------------- +start_channel_sock(Config) -> + LoginOpts = + case proplists:get_value(group,Config) of + erlang_server -> + [{user, proplists:get_value(user, Config)}, + {password, proplists:get_value(passwd, Config)}]; + openssh_server -> + [] % Use public key + end, + + Opts = [{user_interaction, false}, + {silently_accept_hosts, true} + | LoginOpts], + + {Host,Port} = proplists:get_value(peer, Config), + + %% Get a tcp socket + {ok, Sock} = gen_tcp:connect(Host, Port, [{active,false}]), + + %% and open one channel on one new Connection + {ok, ChPid1, Conn} = ssh_sftp:start_channel(Sock, Opts), + + %% Test that the channel is usable + FileName = proplists:get_value(filename, Config), + ok = open_close_file(ChPid1, FileName, [read]), + ok = open_close_file(ChPid1, FileName, [write]), + + %% Try to open a second channel on the Connection + {ok, ChPid2} = ssh_sftp:start_channel(Conn, Opts), + ok = open_close_file(ChPid1, FileName, [read]), + ok = open_close_file(ChPid2, FileName, [read]), + + %% Test that the second channel still works after closing the first one + ok = ssh_sftp:stop_channel(ChPid1), + ok = open_close_file(ChPid2, FileName, [write]), + + %% Test the Connection survives that all channels are closed + ok = ssh_sftp:stop_channel(ChPid2), + {ok, ChPid3} = ssh_sftp:start_channel(Conn, Opts), + ok = open_close_file(ChPid3, FileName, [write]), + + %% Test that a closed channel really is closed + {error, closed} = ssh_sftp:open(ChPid2, FileName, [write]), + ok = ssh_sftp:stop_channel(ChPid3), + + %% Test that the socket is closed when the Connection closes + ok = ssh:close(Conn), + timer:sleep(400), %% Until the stop sequence is fixed + {error,einval} = inet:getopts(Sock, [active]), + + ok. + +%%-------------------------------------------------------------------- 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), + {_,Host, Port} = proplists:get_value(sftpd, Config), + User = proplists:get_value(user, Config), + Passwd = proplists:get_value(passwd, Config), {error,"server failed to start sftp subsystem"} = ssh_sftp:start_channel(Host, Port, [{user_interaction, false}, - {user, ?USER}, {password, ?PASSWD}, + {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) -> + ChPid2 = proplists:get_value(channel_pid2, Config), + TarFileName = proplists:get_value(tar_filename, Config), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + erl_tar:close(Handle), + {ChPid,_} = proplists:get_value(sftp,Config), + {ok, #file_info{type=regular}} = + ssh_sftp:read_file_info(ChPid, TarFileName). + +%%-------------------------------------------------------------------- +files_to_tar(Config) -> + ChPid2 = proplists:get_value(channel_pid2, Config), + TarFileName = proplists:get_value(tar_filename, Config), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + F1 = proplists:get_value(tar_F1_txt, Config), + ok = erl_tar:add(Handle, fn(F1,Config), F1, [verbose]), + ok = erl_tar:add(Handle, fn("f2.txt",Config), "f2.txt", [verbose]), + ok = erl_tar:close(Handle), + chk_tar([F1, "f2.txt"], Config). + +%%-------------------------------------------------------------------- +ascii_filename_ascii_contents_to_tar(Config) -> + ChPid2 = proplists:get_value(channel_pid2, Config), + TarFileName = proplists:get_value(tar_filename, Config), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + ok = erl_tar:add(Handle, fn("f2.txt",Config), "f2.txt", [verbose]), + ok = erl_tar:close(Handle), + chk_tar(["f2.txt"], Config). + +%%-------------------------------------------------------------------- +ascii_filename_unicode_contents_to_tar(Config) -> + case proplists:get_value(tar_F3_txt, Config) of + undefined -> + {skip, "Unicode test"}; + Fn -> + ChPid2 = proplists:get_value(channel_pid2, Config), + TarFileName = proplists:get_value(tar_filename, Config), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + ok = erl_tar:add(Handle, fn(Fn,Config), Fn, [verbose]), + ok = erl_tar:close(Handle), + chk_tar([Fn], Config) + end. + +%%-------------------------------------------------------------------- +unicode_filename_ascii_contents_to_tar(Config) -> + case proplists:get_value(tar_F4_txt, Config) of + undefined -> + {skip, "Unicode test"}; + Fn -> + ChPid2 = proplists:get_value(channel_pid2, Config), + TarFileName = proplists:get_value(tar_filename, Config), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + ok = erl_tar:add(Handle, fn(Fn,Config), Fn, [verbose]), + ok = erl_tar:close(Handle), + chk_tar([Fn], Config) + end. + +%%-------------------------------------------------------------------- +big_file_to_tar(Config) -> + ChPid2 = proplists:get_value(channel_pid2, Config), + TarFileName = proplists:get_value(tar_filename, Config), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + ok = erl_tar:add(Handle, fn("big.txt",Config), "big.txt", [verbose]), + ok = erl_tar:close(Handle), + chk_tar(["big.txt"], Config). + + +%%-------------------------------------------------------------------- +files_chunked_to_tar(Config) -> + ChPid2 = proplists:get_value(channel_pid2, Config), + TarFileName = proplists:get_value(tar_filename, Config), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + F1 = proplists:get_value(tar_F1_txt, Config), + ok = erl_tar:add(Handle, fn(F1,Config), F1, [verbose,{chunks,2}]), + ok = erl_tar:close(Handle), + chk_tar([F1], Config). + +%%-------------------------------------------------------------------- +directory_to_tar(Config) -> + ChPid2 = proplists:get_value(channel_pid2, Config), + TarFileName = proplists:get_value(tar_filename, Config), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + ok = erl_tar:add(Handle, fn("d1",Config), "d1", [verbose]), + ok = erl_tar:close(Handle), + chk_tar(["d1"], Config). + +%%-------------------------------------------------------------------- +binaries_to_tar(Config) -> + ChPid2 = proplists:get_value(channel_pid2, Config), + TarFileName = proplists:get_value(tar_filename, Config), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + Bin = <<"A binary">>, + ok = erl_tar:add(Handle, Bin, "b1", [verbose]), + ok = erl_tar:close(Handle), + chk_tar([{"b1",Bin}], Config). + +%%-------------------------------------------------------------------- +null_crypto_tar(Config) -> + ChPid2 = proplists:get_value(channel_pid2, Config), + Cinit = fun() -> {ok, no_state, _SendSize=5} end, + Cenc = fun(Bin,CState) -> {ok,Bin,CState,_SendSize=5} end, + Cend = fun(Bin,_CState) -> {ok,Bin} end, + C = {Cinit,Cenc,Cend}, + TarFileName = proplists:get_value(tar_filename, Config), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write,{crypto,C}]), + Bin = <<"A binary">>, + F1 = proplists:get_value(tar_F1_txt, Config), + ok = erl_tar:add(Handle, Bin, "b1", [verbose]), + ok = erl_tar:add(Handle, fn(F1,Config), F1, [verbose,{chunks,2}]), + ok = erl_tar:add(Handle, fn("big.txt",Config), "big.txt", [verbose,{chunks,15000}]), + ok = erl_tar:close(Handle), + chk_tar([{"b1",Bin}, F1, "big.txt"], Config). + +%%-------------------------------------------------------------------- +simple_crypto_tar_small(Config) -> + ChPid2 = proplists:get_value(channel_pid2, Config), + Cinit = fun() -> {ok, no_state, _Size=6} end, + Cenc = fun(Bin,CState) -> {ok,stuff(Bin),CState,_SendSize=5} end, + Cdec = fun(Bin,CState) -> {ok,unstuff(Bin),CState,_Size=4} end, + Cend = fun(Bin,_CState) -> {ok,stuff(Bin)} end, + C = {Cinit,Cenc,Cend}, + TarFileName = proplists:get_value(tar_filename, Config), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write,{crypto,C}]), + Bin = <<"A binary">>, + F1 = proplists:get_value(tar_F1_txt, Config), + ok = erl_tar:add(Handle, Bin, "b1", [verbose]), + ok = erl_tar:add(Handle, fn(F1,Config), F1, [verbose,{chunks,2}]), + ok = erl_tar:close(Handle), + chk_tar([{"b1",Bin}, F1], Config, [{crypto,{Cinit,Cdec}}]). + +%%-------------------------------------------------------------------- +simple_crypto_tar_big(Config) -> + ChPid2 = proplists:get_value(channel_pid2, Config), + Cinit = fun() -> {ok, no_state, _SendSize=6} end, + Cenc = fun(Bin,CState) -> {ok,stuff(Bin),CState,_SendSize=5} end, + Cdec = fun(Bin,CState) -> {ok,unstuff(Bin),CState,_SendSize=4} end, + Cend = fun(Bin,_CState) -> {ok,stuff(Bin)} end, + C = {Cinit,Cenc,Cend}, + TarFileName = proplists:get_value(tar_filename, Config), + {ok,Handle} = ssh_sftp:open_tar(ChPid2, TarFileName, [write,{crypto,C}]), + Bin = <<"A binary">>, + F1 = proplists:get_value(tar_F1_txt, Config), + ok = erl_tar:add(Handle, Bin, "b1", [verbose]), + ok = erl_tar:add(Handle, fn(F1,Config), F1, [verbose,{chunks,2}]), + ok = erl_tar:add(Handle, fn("big.txt",Config), "big.txt", [verbose,{chunks,15000}]), + ok = erl_tar:close(Handle), + chk_tar([{"b1",Bin}, F1, "big.txt"], Config, [{crypto,{Cinit,Cdec}}]). + +stuff(Bin) -> << <<C,C>> || <<C>> <= Bin >>. + +unstuff(Bin) -> << <<C>> || <<C,C>> <= Bin >>. + +%%-------------------------------------------------------------------- +read_tar(Config) -> + ChPid2 = proplists:get_value(channel_pid2, Config), + NameBins = lists:sort( + [{"b1",<<"A binary">>}, + {"b2",list_to_binary(lists:duplicate(750000,"a"))} + ]), + TarFileName = proplists:get_value(tar_filename, Config), + {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, TarFileName, [write]), + [ok = erl_tar:add(HandleWrite, Bin, Name, [verbose]) + || {Name,Bin} <- NameBins], + ok = erl_tar:close(HandleWrite), + + chk_tar(NameBins, Config). + +%%-------------------------------------------------------------------- +read_null_crypto_tar(Config) -> + ChPid2 = proplists:get_value(channel_pid2, Config), + NameBins = lists:sort( + [{"b1",<<"A binary">>}, + {"b2",list_to_binary(lists:duplicate(750000,"a"))} + ]), + Cinitw = fun() -> {ok, no_state, _SendSize=5} end, + Cinitr = fun() -> {ok, no_state, _FetchSize=42} end, + Cenc = fun(Bin,CState) -> {ok,Bin,CState,_SendSize=42*42} end, + Cdec = fun(Bin,CState) -> {ok,Bin,CState,_FetchSize=19} end, + Cendw = fun(Bin,_CState) -> {ok,Bin} end, + Cw = {Cinitw,Cenc,Cendw}, + Cr = {Cinitr,Cdec}, + + TarFileName = proplists:get_value(tar_filename, Config), + {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, TarFileName, [write,{crypto,Cw}]), + [ok = erl_tar:add(HandleWrite, Bin, Name, [verbose]) + || {Name,Bin} <- NameBins], + ok = erl_tar:close(HandleWrite), + + chk_tar(NameBins, Config, [{crypto,Cr}]). + +%%-------------------------------------------------------------------- +read_crypto_tar(Config) -> + ChPid2 = proplists:get_value(channel_pid2, Config), + NameBins = lists:sort( + [{"b1",<<"A binary">>}, + {"b2",list_to_binary(lists:duplicate(750000,"a"))} + ]), + Cinitw = fun() -> {ok, no_state, _SendSize=5} end, + Cinitr = fun() -> {ok, no_state, _FetchSize=42} end, + + Cenc = fun(Bin,CState) -> {ok,stuff(Bin),CState,_SendSize=42*42} end, + Cdec = fun(Bin,CState) -> {ok,unstuff(Bin),CState,_FetchSize=120} end, + Cendw = fun(Bin,_CState) -> {ok,stuff(Bin)} end, + Cw = {Cinitw,Cenc,Cendw}, + Cr = {Cinitr,Cdec}, + + TarFileName = proplists:get_value(tar_filename, Config), + {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, TarFileName, [write,{crypto,Cw}]), + [ok = erl_tar:add(HandleWrite, Bin, Name, [verbose]) + || {Name,Bin} <- NameBins], + ok = erl_tar:close(HandleWrite), + + chk_tar(NameBins, Config, [{crypto,Cr}]). + +%%-------------------------------------------------------------------- +aes_cbc256_crypto_tar(Config) -> + ChPid2 = proplists:get_value(channel_pid2, Config), + NameBins = lists:sort( + [{"b1",<<"A binary">>}, + {"b2",list_to_binary(lists:duplicate(750000,"a"))}, + {"d1",fn("d1",Config)} % Dir + ]), + Key = <<"This is a 256 bit key. Boring...">>, + Ivec0 = crypto:strong_rand_bytes(16), + DataSize = 1024, % data_size rem 16 = 0 for aes_cbc + + Cinitw = fun() -> {ok, Ivec0, DataSize} end, + Cinitr = fun() -> {ok, Ivec0, DataSize} end, + + Cenc = fun(PlainBin,Ivec) -> + CipherBin = crypto:block_encrypt(aes_cbc256, Key, Ivec, PlainBin), + {ok, CipherBin, crypto:next_iv(aes_cbc,CipherBin), DataSize} + end, + Cdec = fun(CipherBin,Ivec) -> + PlainBin = crypto:block_decrypt(aes_cbc256, Key, Ivec, CipherBin), + {ok, PlainBin, crypto:next_iv(aes_cbc,CipherBin), DataSize} + end, + + Cendw = fun(PlainBin, _) when PlainBin == <<>> -> {ok, <<>>}; + (PlainBin, Ivec) -> + CipherBin = crypto:block_encrypt(aes_cbc256, Key, Ivec, + pad(16,PlainBin)), %% Last chunk + {ok, CipherBin} + end, + + Cw = {Cinitw,Cenc,Cendw}, + TarFileName = proplists:get_value(tar_filename, Config), + {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, TarFileName, [write,{crypto,Cw}]), + [ok = erl_tar:add(HandleWrite, Bin, Name, [verbose]) || {Name,Bin} <- NameBins], + ok = erl_tar:close(HandleWrite), + + Cr = {Cinitr,Cdec}, + chk_tar(NameBins, Config, [{crypto,Cr}]). + + +pad(BlockSize, Bin) -> + PadSize = (BlockSize - (size(Bin) rem BlockSize)) rem BlockSize, + list_to_binary( lists:duplicate(PadSize,0) ). + +%%-------------------------------------------------------------------- +aes_ctr_stream_crypto_tar(Config) -> + ChPid2 = proplists:get_value(channel_pid2, Config), + NameBins = lists:sort( + [{"b1",<<"A binary">>}, + {"b2",list_to_binary(lists:duplicate(750000,"a"))}, + {"d1",fn("d1",Config)} % Dir + ]), + Key = <<"This is a 256 bit key. Boring...">>, + Ivec0 = crypto:strong_rand_bytes(16), + + Cinitw = Cinitr = fun() -> {ok, crypto:stream_init(aes_ctr,Key,Ivec0)} end, + + Cenc = fun(PlainBin,State) -> + {NewState,CipherBin} = crypto:stream_encrypt(State, PlainBin), + {ok, CipherBin, NewState} + end, + Cdec = fun(CipherBin,State) -> + {NewState,PlainBin} = crypto:stream_decrypt(State, CipherBin), + {ok, PlainBin, NewState} + end, + + Cendw = fun(PlainBin, _) when PlainBin == <<>> -> {ok, <<>>}; + (PlainBin, Ivec) -> + CipherBin = crypto:block_encrypt(aes_cbc256, Key, Ivec, + pad(16,PlainBin)), %% Last chunk + {ok, CipherBin} + end, + + Cw = {Cinitw,Cenc,Cendw}, + TarFileName = proplists:get_value(tar_filename, Config), + {ok,HandleWrite} = ssh_sftp:open_tar(ChPid2, TarFileName, [write,{crypto,Cw}]), + [ok = erl_tar:add(HandleWrite, Bin, Name, [verbose]) || {Name,Bin} <- NameBins], + ok = erl_tar:close(HandleWrite), + + Cr = {Cinitr,Cdec}, + chk_tar(NameBins, Config, [{crypto,Cr}]). + +%%-------------------------------------------------------------------- %% 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"), +oldprep(Config) -> + DataDir = proplists:get_value(data_dir, Config), + TestFile = proplists:get_value(filename, Config), + TestFile1 = proplists:get_value(testfile, Config), + TestLink = proplists:get_value(linktest, Config), + TarFileName = proplists:get_value(tar_filename, Config), file:delete(TestFile), file:delete(TestFile1), file:delete(TestLink), + file:delete(TarFileName), %% 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}). + +prepare(Config0) -> + PrivDir = proplists:get_value(priv_dir, Config0), + Dir = filename:join(PrivDir, random_chars(10)), + file:make_dir(Dir), + Keys = [filename, + testfile, + linktest, + tar_filename], + Config1 = foldl_keydelete(Keys, Config0), + Config2 = lists:foldl(fun({Key,Name}, ConfAcc) -> + [{Key, filename:join(Dir,Name)} | ConfAcc] + end, + Config1, + lists:zip(Keys, [proplists:get_value(K,Config0) || K<-Keys])), + + DataDir = proplists:get_value(data_dir, Config2), + FilenameSrc = filename:join(DataDir, "sftp.txt"), + FilenameDst = proplists:get_value(filename, Config2), + {ok,_} = file:copy(FilenameSrc, FilenameDst), + [{sftp_priv_dir,Dir} | Config2]. + + +random_chars(N) -> [crypto:rand_uniform($a,$z) || _<-lists:duplicate(N,x)]. + +foldl_keydelete(Keys, L) -> + lists:foldl(fun(K,E) -> lists:keydelete(K,1,E) end, + L, + Keys). + + +chk_tar(Items, Config) -> + chk_tar(Items, Config, []). + +chk_tar(Items, Config, Opts) -> + TarFileName = proplists:get_value(tar_filename, Config), + chk_tar(Items, TarFileName, Config, Opts). + +chk_tar(Items, TarFileName, Config, Opts) when is_list(Opts) -> + tar_size(TarFileName, Config), + {ChPid,_} = proplists:get_value(sftp,Config), + {ok,HandleRead} = ssh_sftp:open_tar(ChPid, TarFileName, [read|Opts]), + {ok,NameValueList} = erl_tar:extract(HandleRead,[memory,verbose]), + ok = erl_tar:close(HandleRead), + case {lists:sort(expand_items(Items,Config)), lists:sort(NameValueList)} of + {L,L} -> + true; + {Expect,Actual} -> + ct:log("Expect: ~p",[Expect]), ct:log("Actual: ~p",[Actual]), + case erl_tar:table(TarFileName) of + {ok,Names} -> ct:log("names: ~p",[Names]); + Other -> ct:log("~p",[Other]) + end, + ct:log("~s",[analyze_report(Expect, Actual)]), + ct:fail(bad_tar_contents) + end. + +analyze_report([E={NameE,BinE}|Es], [A={NameA,BinA}|As]) -> + if + NameE == NameA, + BinE =/= BinA-> + [["Component ",NameE," differs. \n Expected: ",BinE,"\n Actual: ",BinA,"\n\n"] + | analyze_report(Es,As)]; + + NameE < NameA -> + [["Component ",NameE," is missing.\n\n"] + | analyze_report(Es,[A|As])]; + + NameE > NameA -> + [["Component ",NameA," is not expected.\n\n"] + | analyze_report([E|Es],As)]; + true -> + analyze_report(Es, As) + end; +analyze_report([{NameE,_BinE}|Es], []) -> + [["Component ",NameE," missing.\n\n"] | analyze_report(Es,[])]; +analyze_report([], [{NameA,_BinA}|As]) -> + [["Component ",NameA," not expected.\n\n"] | analyze_report([],As)]; +analyze_report([], []) -> + "". + +tar_size(TarFileName, Config) -> + {ChPid,_} = proplists:get_value(sftp,Config), + {ok,Data} = ssh_sftp:read_file(ChPid, TarFileName), + io:format('Tar file ~p is~n ~p bytes.~n',[TarFileName, size(Data)]). + +expand_items(Items, Config) -> + lists:flatten( + [case Item of + {_Name,Bin} when is_binary(Bin) -> + Item; + {Name,FileName} when is_list(FileName) -> + read_item_contents(Name, fn(FileName,Config)); + FileName when is_list(FileName) -> + read_item_contents(FileName, fn(FileName,Config)) + end || Item <- Items]). + +read_item_contents(ItemName, FileName) -> + case file:read_file(FileName) of + {ok,Bin} -> + {ItemName, Bin}; + {error,eisdir} -> + {ok,FileNames} = file:list_dir(FileName), + [read_item_contents(filename:join(ItemName,Name), + filename:join(FileName,Name)) + || Name<-FileNames] + end. + +fn(Name, Config) -> + Dir = proplists:get_value(datadir_tar, Config), + filename:join(Dir,Name). + +fmt_host({A,B,C,D}) -> lists:concat([A,".",B,".",C,".",D]); +fmt_host(S) -> S. + |