aboutsummaryrefslogblamecommitdiffstats
path: root/lib/ssh/test/ssh_sftpd_SUITE.erl
blob: ad4652521fbe109f58bade359d5a9cd203f1a04a (plain) (tree)
1
2
3
4


                   
                                                        




















                                                                         
                                           
                                        


                         










                                               
                                                                      
                                                                      
                                                                      















                          

                    





                                                                      
                         

                                  






                                                                          
                   
            
                                            
        
 
                        



                                                                 
               
                  

                                                                      






                                     
                                                                      
 


                                      

                                                     

                                                                 

                                          
                 
                                                        
                                                          

                                                                          
    
                                   
                                                         



                                                                  


                                                                    
    




                                                                      

                                     













                                                                     
                                                             


                                                     




                                           
               

                                                                      
                                                                      


                                                                      

























                                                                     
                                          



                                                                      


























                                                                      

















                                                                 
                                          
 


                                                                      





                                                                 
                                              



                                                                      


















                                                                   
                                          



                                                                      















                                                                    
                                               



                                                                      































                                                                
                                       



                                                                      

















                                                                              
                                                                    
 


                                                                      
                                         
                     

















                                                                           
                                                                     
 
                                         




                                                                      
                                     
                     




















                                                                              
                                                        




                                                                      



























































                                                                             
                                   
 


                                                                      
                                              
                     






                                                                   
 
                                                           
 

                                                      
 
                                                   
 

                                                                        
 


                                                                          
 

                                                              
 


                                                                     
                                                                           
                                                      
 
                                           




                                                                            
 

                                                                           
 
                          
 
                                                                    





                                                                                    
                                                         
        

                                                                      


                                                          







                                                      
                                                                

                                                                      


                                              


                                          
                     











                                                                           


                       





















                                                                      











                                                                 

                                                                      


































                                                                            
                        




























































































                                                                       
                                             













































































































































                                                                             

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

%%
-module(ssh_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").

-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 -----------------------------------
%%--------------------------------------------------------------------

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, 
     relpath, 
     sshd_read_file,
     ver6_basic].

groups() -> 
    [].

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

init_per_suite(Config) ->
    case (catch crypto:start()) of
	ok ->
	    DataDir = ?config(data_dir, Config),	    
	    PrivDir = ?config(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(?config(priv_dir, Config), nopubkey), 
	    file:make_dir(UserDir),  
	    Config;
	_ ->
	    {skip,"Could not start crypto!"}
    end.

end_per_suite(Config) ->
    SysDir = ?config(priv_dir, Config),
    ssh_test_lib:clean_dsa(SysDir),
    UserDir = filename:join(?config(priv_dir, Config), nopubkey),
    file:del_dir(UserDir),
    ssh:stop(),
    crypto:stop().

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

init_per_group(_GroupName, Config) ->
	Config.

end_per_group(_GroupName, Config) ->
	Config.

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

init_per_testcase(TestCase, Config) ->
    ssh:start(),
    prep(Config),
    PrivDir = ?config(priv_dir, Config),
    ClientUserDir = filename:join(PrivDir, nopubkey),
    SystemDir = filename:join(?config(priv_dir, Config), system),

    Port = ssh_test_lib:inet_port(node()),

    {ok, Sftpd} =
	ssh_sftpd:listen(Port, [{system_dir, SystemDir},
				      {user_dir, PrivDir},
				      {user_passwords,[{?USER, ?PASSWD}]},
				      {pwdfun, fun(_,_) -> true end}]),
    
    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;
		      "ver6_" ++ _ ->
			  6;
		      _ ->
			  ?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:pal("Client: ~p Server ~p~n", [ProtocolVer, Version]),

    [{sftp, {Cm, Channel}}, {sftpd, Sftpd }| Config].

end_per_testcase(_TestCase, Config) ->
    ssh_sftpd:stop(?config(sftpd, Config)),
    {Cm, Channel} = ?config(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 =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "test.txt"),
    {Cm, Channel} = ?config(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).

%%--------------------------------------------------------------------
open_close_dir(doc) ->
    ["Test SSH_FXP_OPENDIR and SSH_FXP_CLOSE commands"];
open_close_dir(Config) when is_list(Config) ->
    PrivDir = ?config(priv_dir, Config),
    {Cm, Channel} = ?config(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 =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "test.txt"),

    ReqId = 0,
    {Cm, Channel} = ?config(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 = ?config(priv_dir, Config),
    {Cm, Channel} = ?config(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 =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "test.txt"),

    ReqId = 0,
    {Cm, Channel} = ?config(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 =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "test.txt"),
    ReqId = 0,
    {Cm, Channel} = ?config(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 =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "test.txt"),
    NewFileName = filename:join(PrivDir, "test1.txt"),
    ReqId = 0,
    {Cm, Channel} = ?config(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 = ?config(priv_dir, Config),
    {Cm, Channel} = ?config(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_FAILURE),
	  _/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} = ?config(sftp, Config),
	    PrivDir = ?config(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:pal("Path: ~p PrivDir: ~p~n", [RealPath, AbsPrivDir]),

	    true = RealPath == AbsPrivDir
    end.

%%--------------------------------------------------------------------
links(doc) ->
    [];
links(Config) when is_list(Config) ->
    case os:type() of
	{win32, _} ->
	    {skip, "Links are not fully supported by windows"};
	_ ->
	    ReqId = 0,
	    {Cm, Channel} = ?config(sftp, Config),
	    PrivDir =  ?config(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:pal("Path: ~p~n", [binary_to_list(Path)])
    end.

%%--------------------------------------------------------------------
retrieve_attributes(doc) ->
    ["Test SSH_FXP_STAT, SSH_FXP_LSTAT AND SSH_FXP_FSTAT commands"];
retrieve_attributes(Config) when is_list(Config) ->
    PrivDir =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "test.txt"),
    ReqId = 0,
    {Cm, Channel} = ?config(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 =  ?config(priv_dir, Config),
	    FileName = filename:join(PrivDir, "test.txt"),
	    ReqId = 0,
	    {Cm, Channel} = ?config(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:pal("Org: ~p New: ~p~n", [OrigPermissions, NewPermissions]),
	    true = OrigPermissions =/= NewPermissions,

	    ct:pal("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:pal("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 =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "test.txt"),
    NewFileName = filename:join(PrivDir, "test1.txt"),
    ReqId = 0,
    {Cm, Channel} = ?config(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} = ?config(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 =  ?config(priv_dir, Config),
    FileName = filename:join(PrivDir, "test.txt"),

    ReqId = 0,
    {Cm, Channel} = ?config(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 =  ?config(priv_dir, Config),
    %FileName = filename:join(PrivDir, "test.txt"),
    {Cm, Channel} = ?config(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 =  ?config(priv_dir, Config),
    TestFile = filename:join(PrivDir, "test.txt"),
    TestFile1 = filename:join(PrivDir, "test1.txt"),

    file:delete(TestFile),
    file:delete(TestFile1),

    %% Initial config
    DataDir = ?config(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)
    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).



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:pal("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