ssh_sftpd.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 641 行 · 第 1/2 页

ERL
641
字号
%% ``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 via the world wide web 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.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %%     $Id$%%%%% Description: SFTP server daemon-module(ssh_sftpd).-behaviour(gen_server).%%--------------------------------------------------------------------%% Include files%%---------------------------------------------------------------------include_lib("kernel/include/file.hrl").-include("ssh.hrl").-include("ssh_xfer.hrl").-define(DEFAULT_TIMEOUT, 5000).%%--------------------------------------------------------------------%% External exports-export([listen/1, listen/2, listen/3, stop/1]).%% gen_server callbacks-export([init/1, handle_call/3, handle_cast/2, handle_info/2,	 terminate/2, code_change/3]).-record(state, {	  xf,   			% [{channel,ssh_xfer states}...]	  cwd,				% current dir (on first connect)	  remote_channel,		% remote channel	  pending,                      % binary() 	  file_handler,			% atom() - callback module 	  handles			% list of open handles	  %% handle is either {<int>, directory, {Path, unread|eof}} or	  %% {<int>, file, {Path, IoDevice}}	 }).%%====================================================================%% API%%====================================================================%%--------------------------------------------------------------------%% Function: listen() -> Pid | {error,Error}%% Description: Starts the server%%--------------------------------------------------------------------listen(Port) ->    listen(any, Port, []).listen(Port, Options) ->    listen(any, Port, Options).listen(Addr, Port, Options) ->    ssh_cm:listen(fun() ->			  {ok,Pid} = 			      gen_server:start_link(?MODULE, [Options], []),			  Pid		  end, Addr, Port, Options).%%--------------------------------------------------------------------%% Function: stop(Pid) -> ok%% Description: Stops the listener%%--------------------------------------------------------------------stop(Pid) ->    ssh_cli:stop(Pid).%%====================================================================%% gen_server callbacks%%====================================================================%%--------------------------------------------------------------------%% Function: init(Args) -> {ok, State} |%%                         {ok, State, Timeout} |%%                         ignore               |%%                         {stop, Reason}%% Description: Initiates the server%%--------------------------------------------------------------------init([Options]) ->    FileMod = proplists:get_value(file_handler, Options, 				      ssh_sftpd_file),    {ok, Default} = FileMod:get_cwd(),    CWD = proplists:get_value(cwd, Options, Default),    State = #state{cwd = CWD, handles = [], pending = <<>>,		   file_handler = FileMod},    {ok, State}.%%--------------------------------------------------------------------%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |%%                                      {reply, Reply, State, Timeout} |%%                                      {noreply, State} |%%                                      {noreply, State, Timeout} |%%                                      {stop, Reason, Reply, State} |%%                                      {stop, Reason, State}%% Description: Handling call messages%%--------------------------------------------------------------------handle_call(_Request, _From, State) ->    Reply = ok,    {reply, Reply, State}.%%--------------------------------------------------------------------%% Function: handle_cast(Msg, State) -> {noreply, State} |%%                                      {noreply, State, Timeout} |%%                                      {stop, Reason, State}%% Description: Handling cast messages%%--------------------------------------------------------------------handle_cast(_Msg, State) ->    {noreply, State}.%%--------------------------------------------------------------------%% Function: handle_info(Info, State) -> {noreply, State} |%%                                       {noreply, State, Timeout} |%%                                       {stop, Reason, State}%% Description: Handling all non call/cast messages%%--------------------------------------------------------------------handle_info({ssh_cm, CM, {open, Channel, RemoteChannel, _}}, State) ->    XF = #ssh_xfer{vsn = 5, ext = [], cm = CM, channel = Channel},    State1 = State#state{xf = XF, remote_channel = RemoteChannel},    {noreply, State1};handle_info({ssh_cm, CM, {data, Channel, Type, Data}}, State) ->    ssh_cm:adjust_window(CM, Channel, size(Data)),    State1 = handle_data(Type, Data, State),    {noreply, State1};handle_info({ssh_cm, CM, {subsystem, _Channel, WantsReply, "sftp"}}, State) ->    CM = (State#state.xf)#ssh_xfer.cm, 		% hmmm going through xf...    case WantsReply of	true -> CM ! {ssh_cm, self(), {success, State#state.remote_channel}}    end,    {noreply, State};%% The client has terminated the sessionhandle_info({ssh_cm, _, {eof, Channel}}, 	    State = #state{xf = #ssh_xfer{channel = Channel}}) ->    {stop, normal, State};handle_info(_Info, State) ->    ?dbg(true, "handle_info: Info=~p State=~p\n", [_Info, State]),    {noreply, State}.handle_data(0, <<?UINT32(Len), Msg:Len/binary, Rest/binary>>, 	    State = #state{pending = <<>>}) ->    <<Op, ?UINT32(ReqId), Data/binary>> = Msg,    NewState = handle_op(Op, ReqId, Data, State),    case Rest of	<<>> ->	    NewState;   	_ ->	    handle_data(0, Rest, NewState)    end;	     handle_data(0, Data, State = #state{pending = <<>>}) ->    State#state{pending = Data};handle_data(Type, Data, State = #state{pending = Pending}) ->      handle_data(Type, <<Pending/binary, Data/binary>>,                  State#state{pending = <<>>}); handle_data(_, Data, State) ->    error_logger:format("ssh: STDERR: ~s\n", [binary_to_list(Data)]),    State.handle_op(?SSH_FXP_INIT, Version, B, State) when is_binary(B) ->    XF = State#state.xf,    Vsn = lists:min([XF#ssh_xfer.vsn, Version]),    XF1 = XF#ssh_xfer{vsn = Vsn},    ssh_xfer:xf_send_reply(XF1, ?SSH_FXP_VERSION, <<?UINT32(Vsn)>>),    State#state{xf = XF1};handle_op(?SSH_FXP_REALPATH, ReqId,	  <<?UINT32(Rlen), RPath:Rlen/binary>>,	  State) ->    RelPath = binary_to_list(RPath),    AbsPath = relate_file_name(RelPath, State),    ?dbg(true, "handle_op ?SSH_FXP_REALPATH: RelPath=~p AbsPath=~p\n",	 [RelPath, AbsPath]),    XF = State#state.xf,    Attr = #ssh_xfer_attr{type=directory},    ssh_xfer:xf_send_name(XF, ReqId, AbsPath, Attr),    State;handle_op(?SSH_FXP_OPENDIR, ReqId,	 <<?UINT32(RLen), RPath:RLen/binary>>,	  State = #state{file_handler = FileMod}) ->    RelPath = binary_to_list(RPath),    AbsPath = relate_file_name(RelPath, State),    XF = State#state.xf,    case FileMod:is_dir(AbsPath) of	false ->	    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_NOT_A_DIRECTORY,				    "Not a directory"),	    State;	true ->	    add_handle(State, XF, ReqId, directory, {RelPath,unread})    end;handle_op(?SSH_FXP_READDIR, ReqId,	  <<?UINT32(HLen), BinHandle:HLen/binary>>,	  State) ->    XF = State#state.xf,    case get_handle(State#state.handles, BinHandle) of	{_Handle, directory, {_RelPath, eof}} ->	    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_EOF),	    State;	{Handle, directory, {RelPath, _}} ->	    read_dir(State, XF, ReqId, Handle, RelPath);	_ ->	    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_INVALID_HANDLE),	    State    end;handle_op(?SSH_FXP_CLOSE,  ReqId,	  <<?UINT32(HLen), BinHandle:HLen/binary>>,	    State = #state{handles = Handles, xf = XF,			   file_handler = FileMod}) ->    case get_handle(Handles, BinHandle) of	{Handle, Type, T} ->	    case Type of		file ->		    close_our_file(T, FileMod);		_ ->		    ok	    end,	    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_OK),	    State#state{handles = lists:keydelete(Handle, 1, Handles)};	_ ->	    ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_INVALID_HANDLE),	    State    end;handle_op(?SSH_FXP_LSTAT, ReqId, Data, State) ->    stat((State#state.xf)#ssh_xfer.vsn, ReqId, Data, State, read_link_info);handle_op(?SSH_FXP_STAT, ReqId, Data, State) ->    stat((State#state.xf)#ssh_xfer.vsn, ReqId, Data, State, read_file_info);handle_op(?SSH_FXP_FSTAT, ReqId, Data, State) ->    fstat((State#state.xf)#ssh_xfer.vsn, ReqId, Data, State);handle_op(?SSH_FXP_OPEN, ReqId, Data, State) ->    open((State#state.xf)#ssh_xfer.vsn, ReqId, Data, State);handle_op(?SSH_FXP_READ, ReqId, <<?UINT32(HLen), BinHandle:HLen/binary,				 ?UINT64(Offset), ?UINT32(Len)>>,	  State) ->    case get_handle(State#state.handles, BinHandle) of	{_Handle, file, {_Path, IoDevice}} ->	    read_file(ReqId, IoDevice, Offset, Len, State);	_ ->	    ssh_xfer:xf_send_status(State#state.xf, ReqId, 				    ?SSH_FX_INVALID_HANDLE),	    State    end;handle_op(?SSH_FXP_WRITE, ReqId,	  <<?UINT32(HLen), BinHandle:HLen/binary, ?UINT64(Offset),	   ?UINT32(Len), Data:Len/binary>>, State) ->    case get_handle(State#state.handles, BinHandle) of	{_Handle, file, {_Path, IoDevice}} ->	    write_file(ReqId, IoDevice, Offset, Data, State);	_ ->	    ssh_xfer:xf_send_status(State#state.xf, ReqId,				    ?SSH_FX_INVALID_HANDLE),	    State    end;handle_op(?SSH_FXP_READLINK, ReqId, <<?UINT32(PLen), BPath:PLen/binary>>, 	  State = #state{file_handler = FileMod}) ->    RelPath = binary_to_list(BPath),    AbsPath = relate_file_name(RelPath, State),    case FileMod:read_link(AbsPath) of	{ok, NewPath} ->	    ssh_xfer:xf_send_name(State#state.xf, ReqId, NewPath,				  #ssh_xfer_attr{type=regular});	{error, Error} ->	    ssh_xfer:xf_send_status(State#state.xf, ReqId,				    ssh_xfer:encode_erlang_status(Error))    end,    State;handle_op(?SSH_FXP_SETSTAT, ReqId, <<?UINT32(PLen), BPath:PLen/binary,				    Attr/binary>>, State) ->    Path = relate_file_name(BPath, State),    Status = set_stat(Attr, Path, State),    send_status(Status, ReqId, State);handle_op(?SSH_FXP_MKDIR, ReqId, <<?UINT32(PLen), BPath:PLen/binary,				  Attr/binary>>, 	    State = #state{file_handler = FileMod}) ->    Path = relate_file_name(BPath, State),    case FileMod:make_dir(Path) of	ok ->	    set_stat(Attr, Path, State),	    send_status(ok, ReqId, State);	{error, Error} ->	    send_status({error, Error}, ReqId, State)    end;handle_op(?SSH_FXP_FSETSTAT, ReqId, <<?UINT32(HLen), BinHandle:HLen/binary, 				     Attr/binary>>, 	  State = #state{handles = Handles}) ->    case get_handle(Handles, BinHandle) of	{_Handle, _Type, {Path,_}} ->	    Status = set_stat(Attr, Path, State),	    send_status(Status, ReqId, State);	_ ->	    ssh_xfer:xf_send_status(State#state.xf, ReqId,				    ?SSH_FX_INVALID_HANDLE),	    State    end;handle_op(?SSH_FXP_REMOVE, ReqId, <<?UINT32(PLen), BPath:PLen/binary>>, 	  State = #state{file_handler = FileMod}) ->    Path = relate_file_name(BPath, State),    %%  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 = FileMod:delete(Path),    send_status(Status, ReqId, State);    %%end;handle_op(?SSH_FXP_RMDIR, ReqId, <<?UINT32(PLen), BPath:PLen/binary>>, 	  State = #state{file_handler = FileMod}) ->    Path = relate_file_name(BPath, State),    Status = FileMod:del_dir(Path),

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?