ssh_sftp.erl

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

ERL
983
字号
handle_call({get_file_info,Async,Name}, From, State) ->    ReqID = State#state.req_id,    ssh_xfer:fstat(?XF(State), ReqID, Name, all),    make_reply(ReqID, Async, From, State);handle_call({read_link_info,Async,Name}, From, State) ->    ReqID = State#state.req_id,    ssh_xfer:lstat(?XF(State), ReqID, Name, all),    make_reply(ReqID, Async, From, State);handle_call({read_link,Async,Name}, From, State) ->    ReqID = State#state.req_id,    ssh_xfer:readlink(?XF(State), ReqID, Name),    make_reply(ReqID, Async, From, State);handle_call({make_symlink, Async, Path, TargetPath}, From, State) ->    ReqID = State#state.req_id,    ssh_xfer:symlink(?XF(State), ReqID, Path, TargetPath),    make_reply(ReqID, Async, From, State);handle_call({write_file_info,Async,Name,Info}, From, State) ->    ReqID = State#state.req_id,    A = info_to_attr(Info),    ssh_xfer:setstat(?XF(State), ReqID, Name, A),    make_reply(ReqID, Async, From, State);handle_call(send_window, _From, State) ->    XF = State#state.xf,    {reply, ssh_cm:send_window(XF#ssh_xfer.cm, XF#ssh_xfer.channel,			      ?FILEOP_TIMEOUT), State};handle_call(recv_window, _From, State) ->    XF = State#state.xf,    {reply, ssh_cm:recv_window(XF#ssh_xfer.cm, XF#ssh_xfer.channel,			       ?FILEOP_TIMEOUT), State};handle_call(stop, _From, State) ->    XF = State#state.xf,    #ssh_xfer{cm = CM, channel = Channel} = XF,    ssh_cm:close(CM, Channel),    ssh_cm:stop(CM),    {stop, normal, ok, State};handle_call(Call, _From, State) ->        {reply, {error, bad_call, Call}, State}.%%--------------------------------------------------------------------%% Function: handle_cast/2%% Description: Handling cast messages%% Returns: {noreply, State}          |%%          {noreply, State, Timeout} |%%          {stop, Reason, State}            (terminate/2 is called)%%--------------------------------------------------------------------handle_cast(_Msg, State) ->    {noreply, State}.%%--------------------------------------------------------------------%% Function: handle_info/2%% Description: Handling all non call/cast messages%% Returns: {noreply, State}          |%%          {noreply, State, Timeout} |%%          {stop, Reason, State}            (terminate/2 is called)%%--------------------------------------------------------------------handle_info({ssh_cm, CM, {data,Channel,Type,Data}}, State) ->    ssh_cm:adjust_window(CM, Channel, size(Data)),    if Type == 0 ->	    Data0 = State#state.rep_buf,	    State1 = handle_reply(State,CM,Channel,				  <<Data0/binary,Data/binary>>),	    {noreply, State1};       true ->	    error_logger:format("ssh: STDERR: ~s\n", [binary_to_list(Data)]),	    {noreply, State}    end;handle_info({ssh_cm, CM, {exit_signal,Channel,_SIG,Err,_Lang}},State) ->    ssh_cm:close(CM, Channel),    State1 = reply_all(State, CM, Channel, {error, Err}),    ?dbg(true, "handle_info: exit_signal ~p ~p ~p\n", [_SIG, Err, _Lang]),    {stop, normal, State1};handle_info({ssh_cm, CM, {exit_status,Channel,_Status}},State) ->    ssh_cm:close(CM, Channel),    State1 = reply_all(State, CM, Channel, eof),    ?dbg(true, "handle_info: exit_status ~p\n", [_Status]),    {stop, normal, State1};handle_info({ssh_cm, CM, {eof, Channel}},State) ->    State1 = reply_all(State, CM, Channel, eof),    ?dbg(true, "handle_info: eof \n", []),    {stop, normal, State1};handle_info({ssh_cm, CM, {closed, Channel}},State) ->    State1 = reply_all(State, CM, Channel, {error, closed}),    ?dbg(true, "handle_info: closed\n", []),    {stop, normal, State1};handle_info(_Info, State) ->    ?dbg(true, "sftp: got info ~p\n", [_Info]),    {noreply, State}.%%--------------------------------------------------------------------%% Function: terminate/2%% Description: Shutdown the server%% Returns: any (ignored by gen_server)%%--------------------------------------------------------------------terminate(_Reason, _State) ->    ok.%%--------------------------------------------------------------------%% Func: code_change/3%% Purpose: Convert process state when code is changed%% Returns: {ok, NewState}%%--------------------------------------------------------------------code_change(_OldVsn, State, _Extra) ->    {ok, State}.%%--------------------------------------------------------------------%%% Internal functions%%--------------------------------------------------------------------open2(OrigReqID,FileName,Handle,Mode,Async,From,State) ->    I0 = State#state.inf,    FileMode = case member(binary, Mode) orelse member(raw, Mode) of		   true -> binary;		   false -> text	       end,    I1 = add_new_handle(Handle, FileMode, I0),    State0 = State#state{inf = I1},    ReqID = State0#state.req_id,    ssh_xfer:stat(State0#state.xf, ReqID, FileName, [size]),    case Async of	true ->	    wait_req(ReqID, State0,		     fun({ok,FI},State1) ->			     Size = FI#file_info.size,			     State2 = if is_integer(Size) ->					      put_size(Handle, Size, State1);					 true ->					      State1				      end,			     async_reply(OrigReqID, {ok,Handle}, From, State2);			(_, State1) ->			     async_reply(OrigReqID, {ok,Handle}, From, State1)		     end);	false ->	    wait_req(ReqID, State0,		     fun({ok,FI},State1) ->			     Size = FI#file_info.size,			     State2 = if is_integer(Size) ->					      put_size(Handle, Size, State1);					 true ->					      State1				      end,			     sync_reply({ok,Handle}, From, State2);			(_, State1) ->			     sync_reply({ok,Handle}, From, State1)		     end)    end.async_reply(ReqID, Reply, _From={To,_}, State) ->    To ! {async_reply, ReqID, Reply},    State.sync_reply(Reply, From, State) ->    gen:reply(From, Reply),    State.reply_all(State, _Cm, _Channel, Reply) ->    List = State#state.req_list,    foreach(fun({_ReqID,Fun}) ->		    catch Fun(Reply,State)	    end, List),    State#state {爎eq_list = []}.make_reply(ReqID, true, From, State) ->    {reply, {async, ReqID},     wait_req(ReqID, State,	      fun(Reply,State1) -> 		      async_reply(ReqID,Reply,From,State1)	      end)};make_reply(ReqID, false, From, State) ->    {noreply,      wait_req(ReqID, State,	      fun(Reply,State1) -> 		      sync_reply(Reply, From, State1) 	      end)}.make_reply_post(ReqID, true, From, State, PostFun) ->    {reply, {async, ReqID},     wait_req(ReqID, State,	      fun(Reply,State1) ->		      case catch PostFun(Reply, State1) of			  {'EXIT',_} ->			      async_reply(ReqID,Reply, From, State1);			  {Reply1, State2} ->			      async_reply(ReqID,Reply1, From, State2)		      end	      end)};make_reply_post(ReqID, false, From, State, PostFun) ->    {noreply,     wait_req(ReqID, State,	      fun(Reply,State1) ->		      case catch PostFun(Reply, State1) of			  {'EXIT',_} ->			      sync_reply(Reply, From, State1);			  {Reply1, State2} ->			      sync_reply(Reply1, From, State2)		      end	      end)}.wait_req(ReqID, State, Fun) ->    List = [{ReqID,Fun} | State#state.req_list],    ID = (State#state.req_id + 1) band 16#ffffffff,    State#state { req_list = List, req_id = ID }.handle_reply(State, Cm, Channel, Data) ->    case Data of	<<?UINT32(Len), RData:Len/binary, RBuf/binary>> ->	    case catch ssh_xfer:xf_reply(?XF(State), RData) of		{'EXIT', _Reason} ->		    ?dbg(true, "handle_reply: error ~p\n", [_Reason]),		    handle_reply(State, Cm, Channel, RBuf);		XfReply={_, ReqID, _} ->		    State1 = handle_req_reply(State, ReqID, XfReply),		    handle_reply(State1, Cm, Channel, RBuf)	    end;	RBuf ->	    State#state { rep_buf = RBuf }    end.handle_req_reply(State, ReqID, XfReply) ->    case lists:keysearch(ReqID, 1, State#state.req_list) of	false ->	    error_logger:format("handle_req_reply: req_id=~p not found\n",				[ReqID]),	    State;	{value,{_,Fun}} ->	    List = lists:keydelete(ReqID, 1, State#state.req_list),	    State1 = State#state { req_list = List },	    case catch Fun(xreply(XfReply),State1) of		{'EXIT', _} ->  State1;		State2 -> State2	    end    end.xreply({handle,_,H}) -> {ok, H};xreply({data,_,Data}) -> {ok, Data};xreply({name,_,Names}) -> {ok, Names};xreply({attrs, _, A}) -> {ok, attr_to_info(A)};xreply({extended_reply,_,X}) -> {ok, X};xreply({status,_,{ok, _Err, _Lang, _Rep}}) -> ok;xreply({status,_,{eof, _Err, _Lang, _Rep}}) -> eof;xreply({status,_,{Stat, _Err, _Lang, _Rep}}) -> {error, Stat};xreply({Code, _, Reply}) -> {Code, Reply}.%% convert: file_info -> ssh_xfer_attrinfo_to_attr(I) when is_record(I, file_info) ->    #ssh_xfer_attr { permissions = I#file_info.mode,		     size = I#file_info.size,		     type = I#file_info.type,		     owner = I#file_info.uid,		     group = I#file_info.gid,		     atime = datetime_to_unix(I#file_info.atime),		     mtime = datetime_to_unix(I#file_info.mtime),		     createtime = datetime_to_unix(I#file_info.ctime)}.%% convert: ssh_xfer_attr -> file_infoattr_to_info(A) when is_record(A, ssh_xfer_attr) ->    #file_info{      size   = A#ssh_xfer_attr.size,      type   = A#ssh_xfer_attr.type,      access = read_write, %% FIXME: read/write/read_write/none      atime  = unix_to_datetime(A#ssh_xfer_attr.atime),      mtime  = unix_to_datetime(A#ssh_xfer_attr.mtime),      ctime  = unix_to_datetime(A#ssh_xfer_attr.createtime),      mode   = A#ssh_xfer_attr.permissions,      links  = 1,      major_device = 0,      minor_device = 0,      inode  = 0,      uid    = A#ssh_xfer_attr.owner,      gid    = A#ssh_xfer_attr.group}.unix_to_datetime(undefined) ->    undefined;unix_to_datetime(Sec) ->    calendar:gregorian_seconds_to_datetime(Sec + 62167219200).datetime_to_unix(undefined) ->    undefined;datetime_to_unix(DateTime) ->    calendar:datetime_to_gregorian_seconds(DateTime) - 62167219200.open_mode(Vsn,Modes) when Vsn >= 5 ->    open_mode5(Modes);open_mode(_Vsn, Modes) ->    open_mode3(Modes).open_mode5(Modes) ->    A = #ssh_xfer_attr{type = regular},    {Fl, Ac} = case {member(write, Modes),		     member(read, Modes),		     member(append, Modes)} of		   {_, _, true} ->		       {[append_data],			[read_attributes,			 append_data, write_attributes]};		   {true, false, false} ->		       {[create_truncate],			[write_data, write_attributes]};		   {true, true, _} ->		       {[open_or_create],			[read_data, read_attributes,			 write_data, write_attributes]};		   {false, true, _} ->		       {[open_existing],			[read_data, read_attributes]}	       end,    {Ac, Fl, A}.open_mode3(Modes) ->    A = #ssh_xfer_attr{type = regular},    Fl = case {member(write, Modes),	       member(read, Modes),	       member(append, Modes)} of	     {_, _, true} ->		 [append];	     {true, false, false} ->		 [write, creat, trunc];	     {true, true, _} ->		 [read, write];	     {false, true, _} ->		 [read]	 end,    {[], Fl, A}.%% open_mode(3, Mode, Access, Flags, Attrs) ->%%     open_mode(3,Mode,[read_data,read_attributes|Access], [read|Flags], Attrs);%% open_mode(5, [read|Mode], Access, Flags, Attrs) ->%%     Flags1 =%% 	case member(write, Mode) orelse member(truncate_existing, Flags) of%% 	    false -> [open_existing | Flags];%% 	    true  -> Flags%% 	end,%%     open_mode(5, Mode, [read_data,read_attributes|Access], Flags1, Attrs);%% open_mode(3, [write|Mode], Access, Flags, Attrs) ->%%     open_mode(3, Mode, [write_data,write_attributes|Access], %% 	      [write,creat,trunc|Flags], Attrs);%% open_mode(5, [write|Mode], Access, Flags, Attrs) ->%%     Flags1 =%% 	case member(read, Mode) orelse member(existing, Flags) of%% 	    true -> Flags;%% 	    false -> [create_truncate|Flags]%% 	end,%%     open_mode(5, Mode, [write_data,write_attributes|Access], %% 	      Flags1, Attrs);%% open_mode(3, [append|Mode],Access, Flags, Attrs) ->%%     open_mode(3, Mode, [write_data,write_attributes|Access], %% 	      [write,creat,trunc,append|Flags], Attrs);%% open_mode(5, [append|Mode],Access, Flags, Attrs) ->%%     open_mode(5, Mode, [write_data,write_attributes,append_data|Access], %% 	      [open_or_create,write_data,write_attributes,append_data|Flags],%% 	      Attrs);%% open_mode(Vsn, [raw|Mode],Access, Flags, Attrs) ->%%     open_mode(Vsn, Mode, Access, Flags, Attrs);%% open_mode(Vsn, [binary|Mode],Access, Flags, Attrs) ->%%     open_mode(Vsn, Mode, Access, Flags, Attrs);%% open_mode(_, [], Access, Flags, Attrs) ->%%     {Access, Flags, Attrs}.%% accessors for inf dictnew_inf() -> dict:new().add_new_handle(Handle, FileMode, Inf) ->    dict:store(Handle, #fileinf{offset=0, size=0, mode=FileMode}, Inf).update_size(Handle, NewSize, State) ->    OldSize = get_size(Handle, State),    if NewSize > OldSize ->	    put_size(Handle, NewSize, State);       true ->	    State    end.%% set_offset(Handle, NewOffset) ->%%     put({offset,Handle}, NewOffset).update_offset(Handle, NewOffset, State0) ->    State1 = put_offset(Handle, NewOffset, State0),    update_size(Handle, NewOffset, State1).%% access size and offset for handleput_size(Handle, Size, State) ->    Inf0 = State#state.inf,    case dict:find(Handle, Inf0) of	{ok, FI} ->	    State#state{inf=dict:store(Handle, FI#fileinf{size=Size}, Inf0)};	_ ->	    State#state{inf=dict:store(Handle, #fileinf{size=Size,offset=0},				       Inf0)}    end.put_offset(Handle, Offset, State) ->    Inf0 = State#state.inf,    case dict:find(Handle, Inf0) of	{ok, FI} ->	    State#state{inf=dict:store(Handle, FI#fileinf{offset=Offset},				       Inf0)};	_ ->	    State#state{inf=dict:store(Handle, #fileinf{size=Offset,							offset=Offset}, Inf0)}    end.get_size(Handle, State) ->    case dict:find(Handle, State#state.inf) of	{ok, FI} ->	    FI#fileinf.size;	_ ->	    undefined    end.%% get_offset(Handle, State) ->%%     {ok, FI} = dict:find(Handle, State#state.inf),%%     FI#fileinf.offset.get_mode(Handle, State) ->    case dict:find(Handle, State#state.inf) of	{ok, FI} ->	    FI#fileinf.mode;	_ ->	    undefined    end.erase_handle(Handle, State) ->    FI = dict:erase(Handle, State#state.inf),    State#state{inf = FI}.%%%% Caluclate a integer offset%%lseek_position(Handle, Pos, State) ->    case dict:find(Handle, State#state.inf) of	{ok, #fileinf{offset=O, size=S}} ->	    lseek_pos(Pos, O, S);	_ ->	    {error, einval}    end.lseek_pos(_Pos, undefined, _) ->    {error, einval};lseek_pos(Pos, _CurOffset, _CurSize)  when integer(Pos), 0 =< Pos, Pos < ?SSH_FILEXFER_LARGEFILESIZE ->    {ok,Pos};lseek_pos(bof, _CurOffset, _CurSize) ->    {ok,0};lseek_pos(cur, CurOffset, _CurSize) ->    {ok,CurOffset};lseek_pos(eof, _CurOffset, CurSize) ->    {ok,CurSize};lseek_pos({bof, Offset}, _CurOffset, _CurSize)  when integer(Offset), 0 =< Offset, Offset < ?SSH_FILEXFER_LARGEFILESIZE ->    {ok, Offset};lseek_pos({cur, Offset}, CurOffset, _CurSize)  when integer(Offset), -(?SSH_FILEXFER_LARGEFILESIZE) =< Offset,        Offset < ?SSH_FILEXFER_LARGEFILESIZE ->    NewOffset = CurOffset + Offset,    if NewOffset < 0 ->	    {ok, 0};       true ->	    {ok, NewOffset}    end;lseek_pos({eof, Offset}, _CurOffset, CurSize)   when integer(Offset), -(?SSH_FILEXFER_LARGEFILESIZE) =< Offset,        Offset < ?SSH_FILEXFER_LARGEFILESIZE ->    NewOffset = CurSize + Offset,    if NewOffset < 0 ->	    {ok, 0};       true ->	    {ok, NewOffset}    end;lseek_pos(_, _, _) ->    {error, einval}.

⌨️ 快捷键说明

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