diff options
-rw-r--r-- | lib/ssh/doc/src/ssh_sftpd.xml | 7 | ||||
-rw-r--r-- | lib/ssh/src/ssh_sftpd.erl | 77 | ||||
-rw-r--r-- | lib/ssh/src/ssh_xfer.erl | 5 | ||||
-rw-r--r-- | lib/ssh/src/ssh_xfer.hrl | 12 | ||||
-rw-r--r-- | lib/ssh/test/ssh_sftpd_SUITE.erl | 36 | ||||
-rw-r--r-- | lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl | 21 | ||||
-rw-r--r-- | lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl | 2 |
7 files changed, 109 insertions, 51 deletions
diff --git a/lib/ssh/doc/src/ssh_sftpd.xml b/lib/ssh/doc/src/ssh_sftpd.xml index a4273195b5..3666bc7692 100644 --- a/lib/ssh/doc/src/ssh_sftpd.xml +++ b/lib/ssh/doc/src/ssh_sftpd.xml @@ -80,7 +80,12 @@ the user will end up in <c>/tmp/etc</c>. </p> </item> - </taglist> + <tag><c><![CDATA[{sftpd_vsn, integer()}]]></c></tag> + <item> + <p>Sets the sftp version to use, defaults to 5. Version 6 is under + development and limited.</p> + </item> + </taglist> </desc> </func> </funcs> diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl index 6d6f4a0121..c7e8373840 100644 --- a/lib/ssh/src/ssh_sftpd.erl +++ b/lib/ssh/src/ssh_sftpd.erl @@ -119,9 +119,7 @@ init(Options) -> {Root0, State0} end, MaxLength = proplists:get_value(max_files, Options, 0), - - Vsn = proplists:get_value(vsn, Options, 5), - + Vsn = proplists:get_value(sftpd_vsn, Options, 5), {ok, State#state{cwd = CWD, root = Root, max_files = MaxLength, handles = [], pending = <<>>, xf = #ssh_xfer{vsn = Vsn, ext = []}}}. @@ -361,17 +359,21 @@ handle_op(?SSH_FXP_FSETSTAT, ReqId, <<?UINT32(HLen), BinHandle:HLen/binary, State0 end; handle_op(?SSH_FXP_REMOVE, ReqId, <<?UINT32(PLen), BPath:PLen/binary>>, - State0 = #state{file_handler = FileMod, file_state = FS0}) -> + State0 = #state{file_handler = FileMod, file_state = FS0, xf = #ssh_xfer{vsn = Vsn}}) -> Path = relate_file_name(BPath, State0), - %% case FileMod:is_dir(Path) of %% This version 6 we still have ver 5 - %% true -> - %% ssh_xfer:xf_send_status(State#state.xf, ReqId, - %% ?SSH_FX_FILE_IS_A_DIRECTORY); - %% false -> - {Status, FS1} = FileMod:delete(Path, FS0), - State1 = State0#state{file_state = FS1}, - send_status(Status, ReqId, State1); - %%end; + {IsDir, _FS1} = FileMod:is_dir(Path, FS0), + case IsDir of %% This version 6 we still have ver 5 + true when Vsn > 5 -> + ssh_xfer:xf_send_status(State0#state.xf, ReqId, + ?SSH_FX_FILE_IS_A_DIRECTORY, "File is a directory"); + true -> + ssh_xfer:xf_send_status(State0#state.xf, ReqId, + ?SSH_FX_FAILURE, "File is a directory"); + false -> + {Status, FS1} = FileMod:delete(Path, FS0), + State1 = State0#state{file_state = FS1}, + send_status(Status, ReqId, State1) + end; handle_op(?SSH_FXP_RMDIR, ReqId, <<?UINT32(PLen), BPath:PLen/binary>>, State0 = #state{file_handler = FileMod, file_state = FS0}) -> Path = relate_file_name(BPath, State0), @@ -629,31 +631,34 @@ open(Vsn, ReqId, Data, State) when Vsn >= 4 -> do_open(ReqId, State, Path, Flags). do_open(ReqId, State0, Path, Flags) -> - #state{file_handler = FileMod, file_state = FS0, root = Root} = State0, + #state{file_handler = FileMod, file_state = FS0, root = Root, xf = #ssh_xfer{vsn = Vsn}} = State0, XF = State0#state.xf, F = [binary | Flags], - %% case FileMod:is_dir(Path) of %% This is version 6 we still have 5 - %% true -> - %% ssh_xfer:xf_send_status(State#state.xf, ReqId, - %% ?SSH_FX_FILE_IS_A_DIRECTORY); - %% false -> - - AbsPath = case Root of - "" -> - Path; - _ -> - relate_file_name(Path, State0) - end, - - {Res, FS1} = FileMod:open(AbsPath, F, FS0), - State1 = State0#state{file_state = FS1}, - case Res of - {ok, IoDevice} -> - add_handle(State1, XF, ReqId, file, {Path,IoDevice}); - {error, Error} -> - ssh_xfer:xf_send_status(State1#state.xf, ReqId, - ssh_xfer:encode_erlang_status(Error)), - State1 + {IsDir, _FS1} = FileMod:is_dir(Path, FS0), + case IsDir of + true when Vsn > 5 -> + ssh_xfer:xf_send_status(State0#state.xf, ReqId, + ?SSH_FX_FILE_IS_A_DIRECTORY, "File is a directory"); + true -> + ssh_xfer:xf_send_status(State0#state.xf, ReqId, + ?SSH_FX_FAILURE, "File is a directory"); + false -> + AbsPath = case Root of + "" -> + Path; + _ -> + relate_file_name(Path, State0) + end, + {Res, FS1} = FileMod:open(AbsPath, F, FS0), + State1 = State0#state{file_state = FS1}, + case Res of + {ok, IoDevice} -> + add_handle(State1, XF, ReqId, file, {Path,IoDevice}); + {error, Error} -> + ssh_xfer:xf_send_status(State1#state.xf, ReqId, + ssh_xfer:encode_erlang_status(Error)), + State1 + end end. %% resolve all symlinks in a path diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl index d5b6dd03d1..4dfd9ed8b0 100644 --- a/lib/ssh/src/ssh_xfer.erl +++ b/lib/ssh/src/ssh_xfer.erl @@ -383,6 +383,8 @@ decode_status(Status) -> ?SSH_FX_UNKNOWN_PRINCIPLE -> unknown_principle; ?SSH_FX_LOCK_CONFlICT -> lock_conflict; ?SSH_FX_NOT_A_DIRECTORY -> not_a_directory; + ?SSH_FX_FILE_IS_A_DIRECTORY -> file_is_a_directory; + ?SSH_FX_CANNOT_DELETE -> cannot_delete; _ -> {error,Status} end. @@ -392,6 +394,9 @@ encode_erlang_status(Status) -> eof -> ?SSH_FX_EOF; enoent -> ?SSH_FX_NO_SUCH_FILE; eacces -> ?SSH_FX_PERMISSION_DENIED; + eisdir -> ?SSH_FX_FILE_IS_A_DIRECTORY; + eperm -> ?SSH_FX_CANNOT_DELETE; + eexist -> ?SSH_FX_FILE_ALREADY_EXISTS; _ -> ?SSH_FX_FAILURE end. diff --git a/lib/ssh/src/ssh_xfer.hrl b/lib/ssh/src/ssh_xfer.hrl index c13950eb6e..0d85cf2094 100644 --- a/lib/ssh/src/ssh_xfer.hrl +++ b/lib/ssh/src/ssh_xfer.hrl @@ -58,7 +58,6 @@ %%% # SSH_FX_xxx %%% Description: Response packet types for file transfer protocol. %%%---------------------------------------------------------------------- - -define(SSH_FX_OK, 0). -define(SSH_FX_EOF, 1). -define(SSH_FX_NO_SUCH_FILE, 2). @@ -79,7 +78,18 @@ -define(SSH_FX_LOCK_CONFlICT, 17). -define(SSH_FX_DIR_NOT_EMPTY, 18). -define(SSH_FX_NOT_A_DIRECTORY, 19). +-define(SSH_FX_INVALID_FILENAME, 20). +-define(SSH_FX_LINK_LOOP, 21). +-define(SSH_FX_CANNOT_DELETE, 22). +-define(SSH_FX_INVALID_PARAMETER, 23). -define(SSH_FX_FILE_IS_A_DIRECTORY, 24). +-define(SSH_FX_BYTE_RANGE_LOCK_CONFLICT,25). +-define(SSH_FX_BYTE_RANGE_LOCK_REFUSED, 26). +-define(SSH_FX_DELETE_PENDING, 27). +-define(SSH_FX_FILE_CORRUPT, 28). +-define(SSH_FX_OWNER_INVALID, 29). +-define(SSH_FX_GROUP_INVALID, 30). +-define(SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK,31). %%%---------------------------------------------------------------------- %%% # SSH_FILEXFER_xxx diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl index b995eb9f0e..5aa46872ee 100644 --- a/lib/ssh/test/ssh_sftpd_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_SUITE.erl @@ -58,7 +58,8 @@ all() -> links, ver3_rename, relpath, - sshd_read_file]. + sshd_read_file, + ver6_basic]. groups() -> []. @@ -106,12 +107,18 @@ init_per_testcase(TestCase, Config) -> 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}]), + 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(Port, [{subsystems, SubSystems}|Options]); + _ -> + SubSystems = [ssh_sftpd:subsystem_spec([])], + ssh:daemon(Port, [{subsystems, SubSystems}|Options]) + end, Cm = ssh_test_lib:connect(Port, [{user_dir, ClientUserDir}, @@ -341,7 +348,7 @@ mk_rm_dir(Config) when is_list(Config) -> _/binary>>, _} = mkdir(DirName, Cm, Channel, ReqId), NewReqId = 1, - {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_FAILURE), + {ok, <<?SSH_FXP_STATUS, ?UINT32(NewReqId), ?UINT32(?SSH_FX_FILE_ALREADY_EXISTS), _/binary>>, _} = mkdir(DirName, Cm, Channel, NewReqId), NewReqId1 = 2, @@ -591,7 +598,18 @@ sshd_read_file(Config) when is_list(Config) -> 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 ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl index 7fc2312661..8f722941d4 100644 --- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl @@ -42,7 +42,8 @@ all() -> quit, file_cb, root_dir, - list_dir_limited]. + list_dir_limited, + ver6_basic]. groups() -> []. @@ -112,7 +113,12 @@ init_per_testcase(TestCase, Config) -> [{system_dir, SystemDir}, {user_dir, PrivDir}, {subsystems, [Spec]}]; - + "ver6_basic" -> + Spec = + ssh_sftpd:subsystem_spec([{sftpd_vsn, 6}]), + [{system_dir, SystemDir}, + {user_dir, PrivDir}, + {subsystems, [Spec]}]; _ -> [{user_dir, PrivDir}, {system_dir, SystemDir}] @@ -232,7 +238,7 @@ file_cb(Config) when is_list(Config) -> NewDir = filename:join(PrivDir, "testdir"), ok = ssh_sftp:make_dir(Sftp, NewDir), alt_file_handler_check(alt_make_dir), - + ok = ssh_sftp:del_dir(Sftp, NewDir), alt_file_handler_check(alt_read_link_info), alt_file_handler_check(alt_write_file_info), @@ -260,6 +266,15 @@ list_dir_limited(Config) when is_list(Config) -> ssh_sftp:list_dir(Sftp, "."), ct:pal("Listing: ~p~n", [Listing]). +ver6_basic(doc) -> + ["Test some version 6 features"]; +ver6_basic(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + NewDir = filename:join(PrivDir, "testdir2"), + {Sftp, _} = ?config(sftp, Config), + ok = ssh_sftp:make_dir(Sftp, NewDir), + %%Test file_is_a_directory + {error, file_is_a_directory} = ssh_sftp:delete(Sftp, NewDir). %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl index 9e119c4929..9f8a7c496c 100644 --- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl +++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE_data/ssh_sftpd_file_alt.erl @@ -48,7 +48,7 @@ get_cwd(State) -> {file:get_cwd(), State}. is_dir(AbsPath, State) -> - sftpd_file_alt_tester ! alt_is_dir, + %sftpd_file_alt_tester ! alt_is_dir, {filelib:is_dir(AbsPath), State}. list_dir(AbsPath, State) -> |