%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2006-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 %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% 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% %% %% -module(ssh_sftpd_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"). -include("ssh_xfer.hrl"). -include("ssh.hrl"). -include("ssh_test_lib.hrl"). -define(USER, "Alladin"). -define(PASSWD, "Sesame"). -define(XFER_PACKET_SIZE, 32768). -define(XFER_WINDOW_SIZE, 4*?XFER_PACKET_SIZE). -define(TIMEOUT, 10000). -define(REG_ATTERS, <<0,0,0,0,1>>). -define(UNIX_EPOCH, 62167219200). -define(is_set(F, Bits), ((F) band (Bits)) == (F)). %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- suite() -> [{timetrap,{seconds,40}}]. all() -> [open_close_file, open_close_dir, read_file, read_dir, write_file, rename_file, mk_rm_dir, remove_file, real_path, retrieve_attributes, set_attributes, links, ver3_rename, ver3_open_flags, relpath, sshd_read_file, ver6_basic]. groups() -> []. %%-------------------------------------------------------------------- init_per_suite(Config) -> ?CHECK_CRYPTO( begin DataDir = proplists:get_value(data_dir, Config), PrivDir = proplists:get_value(priv_dir, Config), ssh_test_lib:setup_dsa(DataDir, PrivDir), %% to make sure we don't use public-key-auth %% this should be tested by other test suites UserDir = filename:join(proplists:get_value(priv_dir, Config), nopubkey), file:make_dir(UserDir), Config end). end_per_suite(Config) -> SysDir = proplists:get_value(priv_dir, Config), ssh_test_lib:clean_dsa(SysDir), UserDir = filename:join(proplists:get_value(priv_dir, Config), nopubkey), file:del_dir(UserDir), ssh:stop(). %%-------------------------------------------------------------------- init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. %%-------------------------------------------------------------------- init_per_testcase(TestCase, Config) -> ssh:start(), prep(Config), PrivDir = proplists:get_value(priv_dir, Config), ClientUserDir = filename:join(PrivDir, nopubkey), SystemDir = filename:join(proplists:get_value(priv_dir, Config), system), Options = [{system_dir, SystemDir}, {user_dir, PrivDir}, {user_passwords,[{?USER, ?PASSWD}]}, {pwdfun, fun(_,_) -> true end}], {ok, Sftpd} = case TestCase of ver6_basic -> SubSystems = [ssh_sftpd:subsystem_spec([{sftpd_vsn, 6}])], ssh:daemon(0, [{subsystems, SubSystems}|Options]); _ -> SubSystems = [ssh_sftpd:subsystem_spec([])], ssh:daemon(0, [{subsystems, SubSystems}|Options]) end, {ok,Dinf} = ssh:daemon_info(Sftpd), Port = proplists:get_value(port, Dinf), Cm = ssh_test_lib:connect(Port, [{user_dir, ClientUserDir}, {user, ?USER}, {password, ?PASSWD}, {user_interaction, false}, {silently_accept_hosts, true}, {pwdfun, fun(_,_) -> true end}]), {ok, Channel} = ssh_connection:session_channel(Cm, ?XFER_WINDOW_SIZE, ?XFER_PACKET_SIZE, ?TIMEOUT), success = ssh_connection:subsystem(Cm, Channel, "sftp", ?TIMEOUT), ProtocolVer = case atom_to_list(TestCase) of "ver3_" ++ _ -> 3; _ -> ?SSH_SFTP_PROTOCOL_VERSION end, Data = <<?UINT32(ProtocolVer)>> , Size = 1 + size(Data), ssh_connection:send(Cm, Channel, << ?UINT32(Size), ?SSH_FXP_INIT, Data/binary >>), {ok, <<?SSH_FXP_VERSION, ?UINT32(Version), _Ext/binary>>, _} = reply(Cm, Channel), ct:log("Client: ~p Server ~p~n", [ProtocolVer, Version]), [{sftp, {Cm, Channel}}, {sftpd, Sftpd }| Config]. end_per_testcase(_TestCase, Config) -> ssh_sftpd:stop(proplists:get_value(sftpd, Config)), {Cm, Channel} = proplists:get_value(sftp, Config), ssh_connection:close(Cm, Channel), ssh:close(Cm), ssh:stop(). %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- open_close_file() -> [{doc, "Test SSH_FXP_OPEN and SSH_FXP_CLOSE commands"}]. open_close_file(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), {Cm, Channel} = proplists:get_value(sftp, Config), ReqId = 0, {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = open_file(FileName, Cm, Channel, ReqId, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING), {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_OK), _/binary>>, _} = close(Handle, ReqId, Cm, Channel), NewReqId = ReqId + 1, {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_INVALID_HANDLE), _/binary>>, _} = close(Handle, ReqId, Cm, Channel), NewReqId1 = NewReqId + 1, %% {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), % Ver 6 we have 5 %% ?UINT32(?SSH_FX_FILE_IS_A_DIRECTORY), _/binary>>, _} = {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId1), ?UINT32(?SSH_FX_FAILURE), _/binary>>, _} = open_file(PrivDir, Cm, Channel, NewReqId1, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING). ver3_open_flags() -> [{doc, "Test open flags"}]. ver3_open_flags(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "not_exist.txt"), {Cm, Channel} = proplists:get_value(sftp, Config), ReqId = 0, {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = open_file_v3(FileName, Cm, Channel, ReqId, ?SSH_FXF_CREAT bor ?SSH_FXF_TRUNC), {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_OK), _/binary>>, _} = close(Handle, ReqId, Cm, Channel), NewFileName = filename:join(PrivDir, "not_exist2.txt"), NewReqId = ReqId + 1, {ok, <<?SSH_FXP_HANDLE, ?UINT32(NewReqId), NewHandle/binary>>, _} = open_file_v3(NewFileName, Cm, Channel, NewReqId, ?SSH_FXF_CREAT bor ?SSH_FXF_EXCL), {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_OK), _/binary>>, _} = close(NewHandle, NewReqId, Cm, Channel), NewFileName1 = filename:join(PrivDir, "test.txt"), NewReqId1 = NewReqId + 1, {ok, <<?SSH_FXP_HANDLE, ?UINT32(NewReqId1), NewHandle1/binary>>, _} = open_file_v3(NewFileName1, Cm, Channel, NewReqId1, ?SSH_FXF_READ bor ?SSH_FXF_WRITE bor ?SSH_FXF_APPEND), {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId1), ?UINT32(?SSH_FX_OK), _/binary>>, _} = close(NewHandle1, NewReqId1, Cm, Channel). %%-------------------------------------------------------------------- open_close_dir() -> [{doc,"Test SSH_FXP_OPENDIR and SSH_FXP_CLOSE commands"}]. open_close_dir(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), {Cm, Channel} = proplists:get_value(sftp, Config), FileName = filename:join(PrivDir, "test.txt"), ReqId = 0, {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = open_dir(PrivDir, Cm, Channel, ReqId), {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_OK), _/binary>>, _} = close(Handle, ReqId, Cm, Channel), NewReqId = 1, case open_dir(FileName, Cm, Channel, NewReqId) of {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_NOT_A_DIRECTORY), _/binary>>, _} -> %% Only if server is using vsn > 5. ok; {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_FAILURE), _/binary>>, _} -> ok end. %%-------------------------------------------------------------------- read_file() -> [{doc, "Test SSH_FXP_READ command"}]. read_file(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), ReqId = 0, {Cm, Channel} = proplists:get_value(sftp, Config), {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = open_file(FileName, Cm, Channel, ReqId, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING), NewReqId = 1, {ok, <<?SSH_FXP_DATA, ?UINT32(NewReqId), ?UINT32(_Length), Data/binary>>, _} = read_file(Handle, 100, 0, Cm, Channel, NewReqId), {ok, Data} = file:read_file(FileName). %%-------------------------------------------------------------------- read_dir() -> [{doc,"Test SSH_FXP_READDIR command"}]. read_dir(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), {Cm, Channel} = proplists:get_value(sftp, Config), ReqId = 0, {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = open_dir(PrivDir, Cm, Channel, ReqId), ok = read_dir(Handle, Cm, Channel, ReqId). %%-------------------------------------------------------------------- write_file() -> [{doc, "Test SSH_FXP_WRITE command"}]. write_file(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), ReqId = 0, {Cm, Channel} = proplists:get_value(sftp, Config), {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = open_file(FileName, Cm, Channel, ReqId, ?ACE4_WRITE_DATA bor ?ACE4_WRITE_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING), NewReqId = 1, Data = list_to_binary("Write file test"), {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_OK), _/binary>>, _} = write_file(Handle, Data, 0, Cm, Channel, NewReqId), {ok, Data} = file:read_file(FileName). %%-------------------------------------------------------------------- remove_file() -> [{doc, "Test SSH_FXP_REMOVE command"}]. remove_file(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), ReqId = 0, {Cm, Channel} = proplists:get_value(sftp, Config), {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_OK), _/binary>>, _} = remove(FileName, Cm, Channel, ReqId), NewReqId = 1, %% {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), % ver 6 we have 5 %% ?UINT32(?SSH_FX_FILE_IS_A_DIRECTORY ), _/binary>>, _} = {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_FAILURE), _/binary>>, _} = remove(PrivDir, Cm, Channel, NewReqId). %%-------------------------------------------------------------------- rename_file() -> [{doc, "Test SSH_FXP_RENAME command"}]. rename_file(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), NewFileName = filename:join(PrivDir, "test1.txt"), ReqId = 0, {Cm, Channel} = proplists:get_value(sftp, Config), {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_OK), _/binary>>, _} = rename(FileName, NewFileName, Cm, Channel, ReqId, 6, 0), NewReqId = ReqId + 1, {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_OK), _/binary>>, _} = rename(NewFileName, FileName, Cm, Channel, NewReqId, 6, ?SSH_FXP_RENAME_OVERWRITE), NewReqId1 = NewReqId + 1, file:copy(FileName, NewFileName), %% No owerwrite {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId1), ?UINT32(?SSH_FX_FILE_ALREADY_EXISTS), _/binary>>, _} = rename(FileName, NewFileName, Cm, Channel, NewReqId1, 6, ?SSH_FXP_RENAME_NATIVE), NewReqId2 = NewReqId1 + 1, {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId2), ?UINT32(?SSH_FX_OP_UNSUPPORTED), _/binary>>, _} = rename(FileName, NewFileName, Cm, Channel, NewReqId2, 6, ?SSH_FXP_RENAME_ATOMIC). %%-------------------------------------------------------------------- mk_rm_dir() -> [{doc, "Test SSH_FXP_MKDIR and SSH_FXP_RMDIR command"}]. mk_rm_dir(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), {Cm, Channel} = proplists:get_value(sftp, Config), DirName = filename:join(PrivDir, "test"), ReqId = 0, {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_OK), _/binary>>, _} = mkdir(DirName, Cm, Channel, ReqId), NewReqId = 1, {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_FILE_ALREADY_EXISTS), _/binary>>, _} = mkdir(DirName, Cm, Channel, NewReqId), NewReqId1 = 2, {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId1), ?UINT32(?SSH_FX_OK), _/binary>>, _} = rmdir(DirName, Cm, Channel, NewReqId1), NewReqId2 = 3, {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId2), ?UINT32(?SSH_FX_NO_SUCH_FILE), _/binary>>, _} = rmdir(DirName, Cm, Channel, NewReqId2). %%-------------------------------------------------------------------- real_path() -> [{doc, "Test SSH_FXP_REALPATH command"}]. real_path(Config) when is_list(Config) -> case os:type() of {win32, _} -> {skip, "Not a relevant test on windows"}; _ -> ReqId = 0, {Cm, Channel} = proplists:get_value(sftp, Config), PrivDir = proplists:get_value(priv_dir, Config), TestDir = filename:join(PrivDir, "ssh_test"), ok = file:make_dir(TestDir), OrigPath = filename:join(TestDir, ".."), {ok, <<?SSH_FXP_NAME, ?UINT32(ReqId), ?UINT32(_), ?UINT32(Len), Path:Len/binary, _/binary>>, _} = real_path(OrigPath, Cm, Channel, ReqId), RealPath = filename:absname(binary_to_list(Path)), AbsPrivDir = filename:absname(PrivDir), ct:log("Path: ~p PrivDir: ~p~n", [RealPath, AbsPrivDir]), true = RealPath == AbsPrivDir end. %%-------------------------------------------------------------------- links(Config) when is_list(Config) -> case os:type() of {win32, _} -> {skip, "Links are not fully supported by windows"}; _ -> ReqId = 0, {Cm, Channel} = proplists:get_value(sftp, Config), PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), LinkFileName = filename:join(PrivDir, "link_test.txt"), {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_OK), _/binary>>, _} = create_link(LinkFileName, FileName, Cm, Channel, ReqId), NewReqId = 1, {ok, <<?SSH_FXP_NAME, ?UINT32(NewReqId), ?UINT32(_), ?UINT32(Len), Path:Len/binary, _/binary>>, _} = read_link(LinkFileName, Cm, Channel, NewReqId), true = binary_to_list(Path) == FileName, ct:log("Path: ~p~n", [binary_to_list(Path)]) end. %%-------------------------------------------------------------------- retrieve_attributes() -> [{"Test SSH_FXP_STAT, SSH_FXP_LSTAT AND SSH_FXP_FSTAT commands"}]. retrieve_attributes(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), ReqId = 0, {Cm, Channel} = proplists:get_value(sftp, Config), {ok, FileInfo} = file:read_file_info(FileName), AttrValues = retrive_attributes(FileName, Cm, Channel, ReqId), Type = encode_file_type(FileInfo#file_info.type), Size = FileInfo#file_info.size, Owner = FileInfo#file_info.uid, Group = FileInfo#file_info.gid, Permissions = FileInfo#file_info.mode, Atime = calendar:datetime_to_gregorian_seconds( erlang:localtime_to_universaltime(FileInfo#file_info.atime)) - ?UNIX_EPOCH, Mtime = calendar:datetime_to_gregorian_seconds( erlang:localtime_to_universaltime(FileInfo#file_info.mtime)) - ?UNIX_EPOCH, Ctime = calendar:datetime_to_gregorian_seconds( erlang:localtime_to_universaltime(FileInfo#file_info.ctime)) - ?UNIX_EPOCH, lists:foreach(fun(Value) -> <<?UINT32(Flags), _/binary>> = Value, true = ?is_set(?SSH_FILEXFER_ATTR_SIZE, Flags), true = ?is_set(?SSH_FILEXFER_ATTR_PERMISSIONS, Flags), true = ?is_set(?SSH_FILEXFER_ATTR_ACCESSTIME, Flags), true = ?is_set(?SSH_FILEXFER_ATTR_CREATETIME, Flags), true = ?is_set(?SSH_FILEXFER_ATTR_MODIFYTIME, Flags), true = ?is_set(?SSH_FILEXFER_ATTR_OWNERGROUP, Flags), false = ?is_set(?SSH_FILEXFER_ATTR_ACL, Flags), false = ?is_set(?SSH_FILEXFER_ATTR_SUBSECOND_TIMES, Flags), false = ?is_set(?SSH_FILEXFER_ATTR_BITS, Flags), false = ?is_set(?SSH_FILEXFER_ATTR_EXTENDED, Flags), <<?UINT32(_Flags), ?BYTE(Type), ?UINT64(Size), ?UINT32(OwnerLen), BinOwner:OwnerLen/binary, ?UINT32(GroupLen), BinGroup:GroupLen/binary, ?UINT32(Permissions), ?UINT64(Atime), ?UINT64(Ctime), ?UINT64(Mtime)>> = Value, Owner = list_to_integer(binary_to_list(BinOwner)), Group = list_to_integer(binary_to_list(BinGroup)) end, AttrValues). %%-------------------------------------------------------------------- set_attributes() -> [{doc, "Test SSH_FXP_SETSTAT AND SSH_FXP_FSETSTAT commands"}]. set_attributes(Config) when is_list(Config) -> case os:type() of {win32, _} -> {skip, "Known error bug in erts file:read_file_info"}; _ -> PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), ReqId = 0, {Cm, Channel} = proplists:get_value(sftp, Config), {ok, FileInfo} = file:read_file_info(FileName), OrigPermissions = FileInfo#file_info.mode, Permissions = not_default_permissions(), Flags = ?SSH_FILEXFER_ATTR_PERMISSIONS, Atters = [?uint32(Flags), ?byte(?SSH_FILEXFER_TYPE_REGULAR), ?uint32(Permissions)], {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_OK), _/binary>>, _} = set_attributes_file(FileName, Atters, Cm, Channel, ReqId), {ok, NewFileInfo} = file:read_file_info(FileName), NewPermissions = NewFileInfo#file_info.mode, %% Can not test that NewPermissions = Permissions as %% on Unix platforms, other bits than those listed in the %% API may be set. ct:log("Org: ~p New: ~p~n", [OrigPermissions, NewPermissions]), true = OrigPermissions =/= NewPermissions, ct:log("Try to open the file"), NewReqId = 2, {ok, <<?SSH_FXP_HANDLE, ?UINT32(NewReqId), Handle/binary>>, _} = open_file(FileName, Cm, Channel, NewReqId, ?ACE4_READ_DATA bor ?ACE4_WRITE_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING), NewAtters = [?uint32(Flags), ?byte(?SSH_FILEXFER_TYPE_REGULAR), ?uint32(OrigPermissions)], NewReqId1 = 3, ct:log("Set original permissions on the now open file"), {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId1), ?UINT32(?SSH_FX_OK), _/binary>>, _} = set_attributes_open_file(Handle, NewAtters, Cm, Channel, NewReqId1), {ok, NewFileInfo1} = file:read_file_info(FileName), OrigPermissions = NewFileInfo1#file_info.mode end. %%-------------------------------------------------------------------- ver3_rename() -> [{doc, "Test that ver3 rename message is handled OTP 6352"}]. ver3_rename(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), NewFileName = filename:join(PrivDir, "test1.txt"), ReqId = 0, {Cm, Channel} = proplists:get_value(sftp, Config), {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_OK), _/binary>>, _} = rename(FileName, NewFileName, Cm, Channel, ReqId, 3, 0). %%-------------------------------------------------------------------- relpath() -> [{doc, "Check that realpath works ok seq10670"}]. relpath(Config) when is_list(Config) -> ReqId = 0, {Cm, Channel} = proplists:get_value(sftp, Config), case os:type() of {win32, _} -> {skip, "Not a relevant test on windows"}; _ -> {ok, <<?SSH_FXP_NAME, ?UINT32(ReqId), ?UINT32(_), ?UINT32(Len), Root:Len/binary, _/binary>>, _} = real_path("/..", Cm, Channel, ReqId), <<"/">> = Root, {ok, <<?SSH_FXP_NAME, ?UINT32(ReqId), ?UINT32(_), ?UINT32(Len), Path:Len/binary, _/binary>>, _} = real_path("/usr/bin/../..", Cm, Channel, ReqId), Root = Path end. %%-------------------------------------------------------------------- sshd_read_file() -> [{doc,"Test SSH_FXP_READ command, using sshd-server"}]. sshd_read_file(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), ReqId = 0, {Cm, Channel} = proplists:get_value(sftp, Config), {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = open_file(FileName, Cm, Channel, ReqId, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING), NewReqId = 1, {ok, <<?SSH_FXP_DATA, ?UINT32(NewReqId), ?UINT32(_Length), Data/binary>>, _} = read_file(Handle, 100, 0, Cm, Channel, NewReqId), {ok, Data} = file:read_file(FileName). %%-------------------------------------------------------------------- ver6_basic() -> [{doc, "Test SFTP Version 6"}]. ver6_basic(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), %FileName = filename:join(PrivDir, "test.txt"), {Cm, Channel} = proplists:get_value(sftp, Config), ReqId = 0, {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), % Ver 6 we have 5 ?UINT32(?SSH_FX_FILE_IS_A_DIRECTORY), _/binary>>, _} = open_file(PrivDir, Cm, Channel, ReqId, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING). %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- prep(Config) -> PrivDir = proplists:get_value(priv_dir, Config), TestFile = filename:join(PrivDir, "test.txt"), TestFile1 = filename:join(PrivDir, "test1.txt"), file:delete(TestFile), file:delete(TestFile1), %% Initial config DataDir = proplists:get_value(data_dir, Config), FileName = filename:join(DataDir, "test.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}). reply(Cm, Channel) -> reply(Cm, Channel,<<>>). reply(Cm, Channel, RBuf) -> receive {ssh_cm, Cm, {data, Channel, 0, Data}} -> case <<RBuf/binary, Data/binary>> of <<?UINT32(Len),Reply:Len/binary,Rest/binary>> -> {ok, Reply, Rest}; RBuf2 -> reply(Cm, Channel, RBuf2) end; {ssh_cm, Cm, {eof, Channel}} -> eof; {ssh_cm, Cm, {closed, Channel}} -> closed; {ssh_cm, Cm, Msg} -> ct:fail(Msg) after 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. open_file(File, Cm, Channel, ReqId, Access, Flags) -> Data = list_to_binary([?uint32(ReqId), ?binary(list_to_binary(File)), ?uint32(Access), ?uint32(Flags), ?REG_ATTERS]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_OPEN, Data/binary>>), reply(Cm, Channel). open_file_v3(File, Cm, Channel, ReqId, Flags) -> Data = list_to_binary([?uint32(ReqId), ?binary(list_to_binary(File)), ?uint32(Flags), ?REG_ATTERS]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_OPEN, Data/binary>>), reply(Cm, Channel). close(Handle, ReqId, Cm , Channel) -> Data = list_to_binary([?uint32(ReqId), Handle]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_CLOSE, Data/binary>>), reply(Cm, Channel). open_dir(Dir, Cm, Channel, ReqId) -> Data = list_to_binary([?uint32(ReqId), ?binary(list_to_binary(Dir))]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_OPENDIR, Data/binary>>), reply(Cm, Channel). rename(OldName, NewName, Cm, Channel, ReqId, Version, Flags) -> Data = case Version of 3 -> list_to_binary([?uint32(ReqId), ?binary(list_to_binary(OldName)), ?binary(list_to_binary(NewName))]); _ -> list_to_binary([?uint32(ReqId), ?binary(list_to_binary(OldName)), ?binary(list_to_binary(NewName)), ?uint32(Flags)]) end, Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_RENAME, Data/binary>>), reply(Cm, Channel). mkdir(Dir, Cm, Channel, ReqId)-> Data = list_to_binary([?uint32(ReqId), ?binary(list_to_binary(Dir)), ?REG_ATTERS]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_MKDIR, Data/binary>>), reply(Cm, Channel). rmdir(Dir, Cm, Channel, ReqId) -> Data = list_to_binary([?uint32(ReqId), ?binary(list_to_binary(Dir))]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_RMDIR, Data/binary>>), reply(Cm, Channel). remove(File, Cm, Channel, ReqId) -> Data = list_to_binary([?uint32(ReqId), ?binary(list_to_binary(File))]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_REMOVE, Data/binary>>), reply(Cm, Channel). read_dir(Handle, Cm, Channel, ReqId) -> Data = list_to_binary([?uint32(ReqId), Handle]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_READDIR, Data/binary>>), case reply(Cm, Channel) of {ok, <<?SSH_FXP_NAME, ?UINT32(ReqId), ?UINT32(Count), ?UINT32(Len), Listing:Len/binary, _/binary>>, _} -> ct:log("Count: ~p Listing: ~p~n", [Count, binary_to_list(Listing)]), read_dir(Handle, Cm, Channel, ReqId); {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_EOF), _/binary>>, _} -> ok end. read_file(Handle, MaxLength, OffSet, Cm, Channel, ReqId) -> Data = list_to_binary([?uint32(ReqId), Handle, ?uint64(OffSet), ?uint32(MaxLength)]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_READ, Data/binary>>), reply(Cm, Channel). write_file(Handle, FileData, OffSet, Cm, Channel, ReqId) -> Data = list_to_binary([?uint32(ReqId), Handle, ?uint64(OffSet), ?binary(FileData)]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_WRITE, Data/binary>>), reply(Cm, Channel). real_path(OrigPath, Cm, Channel, ReqId) -> Data = list_to_binary([?uint32(ReqId), ?binary(list_to_binary(OrigPath))]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_REALPATH, Data/binary>>), reply(Cm, Channel). create_link(LinkPath, Path, Cm, Channel, ReqId) -> Data = list_to_binary([?uint32(ReqId), ?binary(list_to_binary(LinkPath)), ?binary(list_to_binary(Path))]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_SYMLINK, Data/binary>>), reply(Cm, Channel). read_link(Link, Cm, Channel, ReqId) -> Data = list_to_binary([?uint32(ReqId), ?binary(list_to_binary(Link))]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_READLINK, Data/binary>>), reply(Cm, Channel). retrive_attributes_file(FilePath, Flags, Cm, Channel, ReqId) -> Data = list_to_binary([?uint32(ReqId), ?binary(list_to_binary(FilePath)), ?uint32(Flags)]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_STAT, Data/binary>>), reply(Cm, Channel). retrive_attributes_file_or_link(FilePath, Flags, Cm, Channel, ReqId) -> Data = list_to_binary([?uint32(ReqId), ?binary(list_to_binary(FilePath)), ?uint32(Flags)]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_LSTAT, Data/binary>>), reply(Cm, Channel). retrive_attributes_open_file(Handle, Flags, Cm, Channel, ReqId) -> Data = list_to_binary([?uint32(ReqId), Handle, ?uint32(Flags)]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_FSTAT, Data/binary>>), reply(Cm, Channel). retrive_attributes(FileName, Cm, Channel, ReqId) -> Attr = ?SSH_FILEXFER_ATTR_SIZE, {ok, <<?SSH_FXP_ATTRS, ?UINT32(ReqId), Value/binary>>, _} = retrive_attributes_file(FileName, Attr, Cm, Channel, ReqId), NewReqId = ReqId + 1, {ok, <<?SSH_FXP_ATTRS, ?UINT32(NewReqId), Value1/binary>>, _} = retrive_attributes_file_or_link(FileName, Attr, Cm, Channel, NewReqId), NewReqId1 = NewReqId + 1, {ok, <<?SSH_FXP_HANDLE, ?UINT32(NewReqId1), Handle/binary>>, _} = open_file(FileName, Cm, Channel, NewReqId1, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING), NewReqId2 = NewReqId1 + 1, {ok, <<?SSH_FXP_ATTRS, ?UINT32(NewReqId2), Value2/binary>>, _} = retrive_attributes_open_file(Handle, Attr, Cm, Channel, NewReqId2), [Value, Value1, Value2]. set_attributes_file(FilePath, Atters, Cm, Channel, ReqId) -> Data = list_to_binary([?uint32(ReqId), ?binary(list_to_binary(FilePath)), Atters]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_SETSTAT, Data/binary>>), reply(Cm, Channel). set_attributes_open_file(Handle, Atters, Cm, Channel, ReqId) -> Data = list_to_binary([?uint32(ReqId), Handle, Atters]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <<?UINT32(Size), ?SSH_FXP_FSETSTAT, Data/binary>>), reply(Cm, Channel). encode_file_type(Type) -> case Type of regular -> ?SSH_FILEXFER_TYPE_REGULAR; directory -> ?SSH_FILEXFER_TYPE_DIRECTORY; symlink -> ?SSH_FILEXFER_TYPE_SYMLINK; special -> ?SSH_FILEXFER_TYPE_SPECIAL; unknown -> ?SSH_FILEXFER_TYPE_UNKNOWN; other -> ?SSH_FILEXFER_TYPE_UNKNOWN; socket -> ?SSH_FILEXFER_TYPE_SOCKET; char_device -> ?SSH_FILEXFER_TYPE_CHAR_DEVICE; block_device -> ?SSH_FILEXFER_TYPE_BLOCK_DEVICE; fifo -> ?SSH_FILEXFER_TYPE_FIFO; undefined -> ?SSH_FILEXFER_TYPE_UNKNOWN end. not_default_permissions() -> 8#600. %% User read-write-only