%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2006-2017. 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(SSH_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, access_outside_root, root_with_cwd, relative_path, open_file_dir_v5, open_file_dir_v6]. 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]); access_outside_root -> %% Build RootDir/access_outside_root/a/b and set Root and CWD BaseDir = filename:join(PrivDir, access_outside_root), RootDir = filename:join(BaseDir, a), CWD = filename:join(RootDir, b), %% Make the directory chain: ok = filelib:ensure_dir(filename:join(CWD, tmp)), SubSystems = [ssh_sftpd:subsystem_spec([{root, RootDir}, {cwd, CWD}])], ssh:daemon(0, [{subsystems, SubSystems}|Options]); root_with_cwd -> RootDir = filename:join(PrivDir, root_with_cwd), CWD = filename:join(RootDir, home), SubSystems = [ssh_sftpd:subsystem_spec([{root, RootDir}, {cwd, CWD}])], ssh:daemon(0, [{subsystems, SubSystems}|Options]); relative_path -> SubSystems = [ssh_sftpd:subsystem_spec([{cwd, PrivDir}])], ssh:daemon(0, [{subsystems, SubSystems}|Options]); open_file_dir_v5 -> SubSystems = [ssh_sftpd:subsystem_spec([{cwd, PrivDir}])], ssh:daemon(0, [{subsystems, SubSystems}|Options]); open_file_dir_v6 -> SubSystems = [ssh_sftpd:subsystem_spec([{cwd, PrivDir}, {sftpd_vsn, 6}])], ssh:daemon(0, [{subsystems, SubSystems}|Options]); _ -> SubSystems = [ssh_sftpd:subsystem_spec([])], ssh:daemon(0, [{subsystems, SubSystems}|Options]) end, Port = ssh_test_lib:daemon_port(Sftpd), Cm = ssh_test_lib:connect(Port, [{user_dir, ClientUserDir}, {user, ?USER}, {password, ?PASSWD}, {user_interaction, false}, {silently_accept_hosts, true}]), {ok, Channel} = ssh_connection:session_channel(Cm, ?XFER_WINDOW_SIZE, ?XFER_PACKET_SIZE, ?SSH_TIMEOUT), success = ssh_connection:subsystem(Cm, Channel, "sftp", ?SSH_TIMEOUT), ProtocolVer = case atom_to_list(TestCase) of "ver3_" ++ _ -> 3; _ -> ?SSH_SFTP_PROTOCOL_VERSION end, Data = <> , Size = 1 + size(Data), ssh_connection:send(Cm, Channel, << ?UINT32(Size), ?SSH_FXP_INIT, Data/binary >>), {ok, <>, _} = reply(Cm, Channel), ct:log("Client: ~p Server ~p~n", [ProtocolVer, Version]), [{sftp, {Cm, Channel}}, {sftpd, Sftpd }| Config]. end_per_testcase(_TestCase, Config) -> catch ssh:stop_daemon(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, <>, _} = open_file(FileName, Cm, Channel, ReqId, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING), {ok, <>, _} = close(Handle, ReqId, Cm, Channel), NewReqId = ReqId + 1, {ok, <>, _} = close(Handle, ReqId, Cm, Channel), NewReqId1 = NewReqId + 1, %% {ok, <>, _} = {ok, <>, _} = 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, <>, _} = open_file_v3(FileName, Cm, Channel, ReqId, ?SSH_FXF_CREAT bor ?SSH_FXF_TRUNC), {ok, <>, _} = close(Handle, ReqId, Cm, Channel), NewFileName = filename:join(PrivDir, "not_exist2.txt"), NewReqId = ReqId + 1, {ok, <>, _} = open_file_v3(NewFileName, Cm, Channel, NewReqId, ?SSH_FXF_CREAT bor ?SSH_FXF_EXCL), {ok, <>, _} = close(NewHandle, NewReqId, Cm, Channel), NewFileName1 = filename:join(PrivDir, "test.txt"), NewReqId1 = NewReqId + 1, {ok, <>, _} = open_file_v3(NewFileName1, Cm, Channel, NewReqId1, ?SSH_FXF_READ bor ?SSH_FXF_WRITE bor ?SSH_FXF_APPEND), {ok, <>, _} = 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, <>, _} = open_dir(PrivDir, Cm, Channel, ReqId), {ok, <>, _} = close(Handle, ReqId, Cm, Channel), NewReqId = 1, case open_dir(FileName, Cm, Channel, NewReqId) of {ok, <>, _} -> %% Only if server is using vsn > 5. ok; {ok, <>, _} -> 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, <>, _} = open_file(FileName, Cm, Channel, ReqId, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING), NewReqId = 1, {ok, <>, _} = 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, <>, _} = 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, <>, _} = 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, <>, _} = 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, <>, _} = remove(FileName, Cm, Channel, ReqId), NewReqId = 1, %% {ok, <>, _} = {ok, <>, _} = 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, <>, _} = rename(FileName, NewFileName, Cm, Channel, ReqId, 6, 0), NewReqId = ReqId + 1, {ok, <>, _} = rename(NewFileName, FileName, Cm, Channel, NewReqId, 6, ?SSH_FXP_RENAME_OVERWRITE), NewReqId1 = NewReqId + 1, file:copy(FileName, NewFileName), %% No owerwrite {ok, <>, _} = rename(FileName, NewFileName, Cm, Channel, NewReqId1, 6, ?SSH_FXP_RENAME_NATIVE), NewReqId2 = NewReqId1 + 1, {ok, <>, _} = 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, <>, _} = mkdir(DirName, Cm, Channel, ReqId), NewReqId = 1, {ok, <>, _} = mkdir(DirName, Cm, Channel, NewReqId), NewReqId1 = 2, {ok, <>, _} = rmdir(DirName, Cm, Channel, NewReqId1), NewReqId2 = 3, {ok, <>, _} = 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, <>, _} = 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, <>, _} = create_link(LinkFileName, FileName, Cm, Channel, ReqId), NewReqId = 1, {ok, <>, _} = 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) -> <> = 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), <> = 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, <>, _} = 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, <>, _} = 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, <>, _} = 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, <>, _} = 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, <>, _} = real_path("/..", Cm, Channel, ReqId), <<"/">> = Root, {ok, <>, _} = 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, <>, _} = open_file(FileName, Cm, Channel, ReqId, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING), NewReqId = 1, {ok, <>, _} = 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, <>, _} = open_file(PrivDir, Cm, Channel, ReqId, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING). %%-------------------------------------------------------------------- access_outside_root() -> [{doc, "Try access files outside the tree below RootDir"}]. access_outside_root(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), BaseDir = filename:join(PrivDir, access_outside_root), %% A file outside the tree below RootDir which is BaseDir/a %% Make the file BaseDir/bad : BadFilePath = filename:join([BaseDir, bad]), ok = file:write_file(BadFilePath, <<>>), {Cm, Channel} = proplists:get_value(sftp, Config), %% Try to access a file parallell to the RootDir: try_access("/../bad", Cm, Channel, 0), %% Try to access the same file via the CWD which is /b relative to the RootDir: try_access("../../bad", Cm, Channel, 1). try_access(Path, Cm, Channel, ReqId) -> Return = open_file(Path, Cm, Channel, ReqId, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING), ct:log("Try open ~p -> ~p",[Path,Return]), case Return of {ok, <>, _} -> ct:fail("Could open a file outside the root tree!"); {ok, <>, <<>>} -> case Code of ?SSH_FX_FILE_IS_A_DIRECTORY -> ct:log("Got the expected SSH_FX_FILE_IS_A_DIRECTORY status",[]), ok; ?SSH_FX_FAILURE -> ct:log("Got the expected SSH_FX_FAILURE status",[]), ok; _ -> case Rest of <> -> ct:fail("Got unexpected SSH_FX_code: ~p (~p)",[Code,Txt]); _ -> ct:fail("Got unexpected SSH_FX_code: ~p",[Code]) end end; _ -> ct:fail("Completly unexpected return: ~p", [Return]) end. %%-------------------------------------------------------------------- root_with_cwd() -> [{doc, "Check if files are found, if the CWD and Root are specified"}]. root_with_cwd(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), RootDir = filename:join(PrivDir, root_with_cwd), CWD = filename:join(RootDir, home), FileName = "root_with_cwd.txt", FilePath = filename:join(CWD, FileName), ok = filelib:ensure_dir(FilePath), ok = file:write_file(FilePath ++ "0", <<>>), ok = file:write_file(FilePath ++ "1", <<>>), ok = file:write_file(FilePath ++ "2", <<>>), {Cm, Channel} = proplists:get_value(sftp, Config), ReqId0 = 0, {ok, <>, _} = open_file(FileName ++ "0", Cm, Channel, ReqId0, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING), ReqId1 = 1, {ok, <>, _} = open_file("./" ++ FileName ++ "1", Cm, Channel, ReqId1, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING), ReqId2 = 2, {ok, <>, _} = open_file("/home/" ++ FileName ++ "2", Cm, Channel, ReqId2, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING). %%-------------------------------------------------------------------- relative_path() -> [{doc, "Test paths relative to CWD when opening a file handle."}]. relative_path(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), FileName = "test_relative_path.txt", FilePath = filename:join(PrivDir, FileName), ok = filelib:ensure_dir(FilePath), ok = file:write_file(FilePath, <<>>), {Cm, Channel} = proplists:get_value(sftp, Config), ReqId = 0, {ok, <>, _} = open_file(FileName, Cm, Channel, ReqId, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING). %%-------------------------------------------------------------------- open_file_dir_v5() -> [{doc, "Test if open_file fails when opening existing directory."}]. open_file_dir_v5(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), FileName = "open_file_dir_v5", FilePath = filename:join(PrivDir, FileName), ok = filelib:ensure_dir(FilePath), ok = file:make_dir(FilePath), {Cm, Channel} = proplists:get_value(sftp, Config), ReqId = 0, {ok, <>, _} = open_file(FileName, Cm, Channel, ReqId, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING). %%-------------------------------------------------------------------- open_file_dir_v6() -> [{doc, "Test if open_file fails when opening existing directory."}]. open_file_dir_v6(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), FileName = "open_file_dir_v6", FilePath = filename:join(PrivDir, FileName), ok = filelib:ensure_dir(FilePath), ok = file:make_dir(FilePath), {Cm, Channel} = proplists:get_value(sftp, Config), ReqId = 0, {ok, <>, _} = open_file(FileName, 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 <> of <> -> {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, <>), 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, <>), reply(Cm, Channel). close(Handle, ReqId, Cm , Channel) -> Data = list_to_binary([?uint32(ReqId), Handle]), Size = 1 + size(Data), ssh_connection:send(Cm, Channel, <>), 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, <>), 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, <>), 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, <>), 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, <>), 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, <>), 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, <>), case reply(Cm, Channel) of {ok, <>, _} -> ct:log("Count: ~p Listing: ~p~n", [Count, binary_to_list(Listing)]), read_dir(Handle, Cm, Channel, ReqId); {ok, <>, _} -> 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, <>), 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, <>), 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, <>), 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, <>), 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, <>), 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, <>), 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, <>), 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, <>), reply(Cm, Channel). retrive_attributes(FileName, Cm, Channel, ReqId) -> Attr = ?SSH_FILEXFER_ATTR_SIZE, {ok, <>, _} = retrive_attributes_file(FileName, Attr, Cm, Channel, ReqId), NewReqId = ReqId + 1, {ok, <>, _} = retrive_attributes_file_or_link(FileName, Attr, Cm, Channel, NewReqId), NewReqId1 = NewReqId + 1, {ok, <>, _} = open_file(FileName, Cm, Channel, NewReqId1, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING), NewReqId2 = NewReqId1 + 1, {ok, <>, _} = 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, <>), 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, <>), 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