ssh_sftp.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 983 行 · 第 1/2 页
ERL
983 行
%% ``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 protocol front-end-module(ssh_sftp).-behaviour(gen_server).%%--------------------------------------------------------------------%% Include files%%---------------------------------------------------------------------include_lib("kernel/include/file.hrl").-include("ssh.hrl").-include("ssh_xfer.hrl").-import(lists, [member/2, map/2, foldl/3, reverse/1, foreach/2]).%%--------------------------------------------------------------------%% External exports%% -export([start/3, start_link/3]).%% -export([start/2, start_link/2]).%% -export([start/1, start_link/1]).-export([connect/1, connect/2, connect/3]).-export([open_mode/2]).%% API-export([open/3, opendir/2, close/2, readdir/2, pread/4, read/3, apread/4, aread/3, pwrite/4, write/3, apwrite/4, awrite/3, position/3, real_path/2, read_file_info/2, get_file_info/2, write_file_info/3, read_link_info/2, read_link/2, make_symlink/3, rename/3, delete/2, make_dir/2, del_dir/2, stop/1, send_window/1, recv_window/1, list_dir/2, read_file/2, write_file/3]).%% gen_server callbacks-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).%% Other exports-export([info_to_attr/1, attr_to_info/1]).-record(state, { xf, rep_buf = <<>>, req_id, req_list = [], %% {ReqId, Fun} inf %% list of fileinf }).-record(fileinf, { handle, offset, size, mode }).-define(FILEOP_TIMEOUT, 60000).-define(NEXT_REQID(S), S#state { req_id = (S#state.req_id + 1) band 16#ffffffff}).-define(XF(S), S#state.xf).-define(REQID(S), S#state.req_id).%%====================================================================%% External functions%%====================================================================open(Pid, File, Mode) -> gen_server:call(Pid, {open, false, File, Mode}, ?FILEOP_TIMEOUT).opendir(Pid, Path) -> gen_server:call(Pid, {opendir, false, Path}, ?FILEOP_TIMEOUT).close(Pid, Handle) -> gen_server:call(Pid, {close,false,Handle}, ?FILEOP_TIMEOUT).readdir(Pid,Handle) -> gen_server:call(Pid, {readdir,false,Handle}, ?FILEOP_TIMEOUT).pread(Pid, Handle, Offset, Len) -> gen_server:call(Pid, {pread,false,Handle, Offset, Len}, ?FILEOP_TIMEOUT).read(Pid, Handle, Len) -> gen_server:call(Pid, {read,false,Handle, Len}, ?FILEOP_TIMEOUT). apread(Pid, Handle, Offset, Len) -> gen_server:call(Pid, {pread,true,Handle, Offset, Len}, ?FILEOP_TIMEOUT).aread(Pid, Handle, Len) -> gen_server:call(Pid, {read,true,Handle, Len}, ?FILEOP_TIMEOUT). pwrite(Pid, Handle, Offset, Data) -> gen_server:call(Pid, {pwrite,false,Handle,Offset,Data}, ?FILEOP_TIMEOUT).write(Pid, Handle, Data) -> gen_server:call(Pid, {write,false,Handle,Data}, ?FILEOP_TIMEOUT).apwrite(Pid, Handle, Offset, Data) -> gen_server:call(Pid, {pwrite,true,Handle,Offset,Data}, ?FILEOP_TIMEOUT).awrite(Pid, Handle, Data) -> gen_server:call(Pid, {write,true,Handle,Data}, ?FILEOP_TIMEOUT).position(Pid, Handle, Pos) -> gen_server:call(Pid, {position, Handle, Pos}, ?FILEOP_TIMEOUT).real_path(Pid, Path) -> gen_server:call(Pid, {real_path, false, Path}, ?FILEOP_TIMEOUT).read_file_info(Pid, Name) -> gen_server:call(Pid, {read_file_info,false,Name}, ?FILEOP_TIMEOUT).get_file_info(Pid, Handle) -> gen_server:call(Pid, {get_file_info,false,Handle}, ?FILEOP_TIMEOUT).write_file_info(Pid, Name, Info) -> gen_server:call(Pid, {write_file_info,false,Name, Info}, ?FILEOP_TIMEOUT).read_link_info(Pid, Name) -> gen_server:call(Pid, {read_link_info,false,Name}, ?FILEOP_TIMEOUT).read_link(Pid, LinkName) -> case gen_server:call(Pid, {read_link,false,LinkName}, ?FILEOP_TIMEOUT) of {ok, [{Name, _Attrs}]} -> {ok, Name}; ErrMsg -> ErrMsg end.make_symlink(Pid, Name, Target) -> gen_server:call(Pid, {make_symlink,false, Name, Target}, ?FILEOP_TIMEOUT). rename(Pid, FromFile, ToFile) -> gen_server:call(Pid, {rename,false,FromFile, ToFile}, ?FILEOP_TIMEOUT).delete(Pid, Name) -> gen_server:call(Pid, {delete,false,Name}, ?FILEOP_TIMEOUT).make_dir(Pid, Name) -> gen_server:call(Pid, {make_dir,false,Name}, ?FILEOP_TIMEOUT).del_dir(Pid, Name) -> gen_server:call(Pid, {del_dir,false,Name}, ?FILEOP_TIMEOUT).stop(Pid) -> gen_server:call(Pid, stop).send_window(Pid) -> gen_server:call(Pid, send_window, ?FILEOP_TIMEOUT).recv_window(Pid) -> gen_server:call(Pid, recv_window, ?FILEOP_TIMEOUT).list_dir(Pid, Name) -> case opendir(Pid, Name) of {ok,Handle} -> Res = do_list_dir(Pid, Handle, []), close(Pid, Handle), case Res of {ok, List} -> NList = foldl(fun({Nm, _Info},Acc) -> [Nm|Acc] end, [], List), {ok,NList}; Error -> Error end; Error -> Error end.do_list_dir(Pid, Handle, Acc) -> case readdir(Pid, Handle) of {name, Names} -> do_list_dir(Pid, Handle, Acc ++ Names); eof -> {ok, Acc}; Error -> Error end.read_file(Pid, Name) -> case open(Pid, Name, [read, binary]) of {ok, Handle} -> {ok,{_WindowSz,PacketSz}} = recv_window(Pid), Res = read_file_loop(Pid, Handle, PacketSz, []), close(Pid, Handle), Res; Error -> Error end.read_file_loop(Pid, Handle, PacketSz, Acc) -> case read(Pid, Handle, PacketSz) of {ok, Data} -> read_file_loop(Pid, Handle, PacketSz, [Data|Acc]); eof -> {ok, list_to_binary(reverse(Acc))}; Error -> Error end.write_file(Pid, Name, List) when list(List) -> write_file(Pid, Name, list_to_binary(List));write_file(Pid, Name, Bin) -> case open(Pid, Name, [write, binary]) of {ok, Handle} -> {ok,{_Window,Packet}} = send_window(Pid), Res = write_file_loop(Pid, Handle, 0, Bin, size(Bin), Packet), close(Pid, Handle), Res; Error -> Error end.write_file_loop(_Pid, _Handle, _Pos, _Bin, 0, _PacketSz) -> ok;write_file_loop(Pid, Handle, Pos, Bin, Remain, PacketSz) -> if Remain >= PacketSz -> <<_:Pos/binary, Data:PacketSz/binary, _/binary>> = Bin, case write(Pid, Handle, Data) of ok -> write_file_loop(Pid, Handle, Pos+PacketSz, Bin, Remain-PacketSz, PacketSz); Error -> Error end; true -> <<_:Pos/binary, Data/binary>> = Bin, write(Pid, Handle, Data) end.%%--------------------------------------------------------------------%% Function: start_link/0%% Description: Starts the server%%--------------------------------------------------------------------%% start_link(CM) when is_pid(CM) ->%% gen_server:start_link(?MODULE, [CM], []);%% start_link(Host) when is_list(Host) ->%% gen_server:start_link(?MODULE, [Host, 22, []], []).%% start_link(Host, Opts) ->%% gen_server:start_link(?MODULE, [Host, 22, Opts], []). %% start_link(Host, Port, Opts) ->%% gen_server:start_link(?MODULE, [Host, Port, Opts], []).connect(CM) when is_pid(CM) -> gen_server:start(?MODULE, [CM], []);connect(Host) when is_list(Host) -> gen_server:start(?MODULE, [Host, 22, []], []).connect(Host, Opts) -> gen_server:start(?MODULE, [Host, 22, Opts], []). connect(Host, Port, Opts) -> gen_server:start(?MODULE, [Host, Port, Opts], []).%%====================================================================%% Server functions%%====================================================================%%--------------------------------------------------------------------%% Function: init/1%% Description: Initiates the server%% Returns: {ok, State} |%% {ok, State, Timeout} |%% ignore |%% {stop, Reason}%%--------------------------------------------------------------------init([CM]) -> case ssh_xfer:attach(CM, ?FILEOP_TIMEOUT) of {ok,Xf,RBuf} -> {ok, #state { req_id = 0, xf = Xf, rep_buf=RBuf, inf = new_inf()}}; Error -> {stop, Error } end;init([Host,Port,Opts]) -> SaveFlag = process_flag(trap_exit, true), case ssh_xfer:connect(Host, Port, Opts) of {ok, Xf, RBuf} -> process_flag(trap_exit, SaveFlag), {ok, #state { req_id = 0, xf = Xf, rep_buf=RBuf, inf = new_inf()}}; Error -> {stop, Error} end.%%--------------------------------------------------------------------%% Function: handle_call/3%% Description: Handling call messages%% Returns: {reply, Reply, State} |%% {reply, Reply, State, Timeout} |%% {noreply, State} |%% {noreply, State, Timeout} |%% {stop, Reason, Reply, State} | (terminate/2 is called)%% {stop, Reason, State} (terminate/2 is called)%%--------------------------------------------------------------------handle_call({open,Async,FileName,Mode}, From, State) -> XF = State#state.xf, {Access,Flags,Attrs} = open_mode(XF#ssh_xfer.vsn, Mode), ReqID = State#state.req_id, ssh_xfer:open(XF, ReqID, FileName, Access, Flags, Attrs), case Async of true -> {reply, {async,ReqID}, wait_req(ReqID, State, fun({ok,Handle},State1) -> open2(ReqID,FileName,Handle,Mode,Async, From,State1); (Rep,State1) -> async_reply(ReqID, Rep, From, State1) end)}; false -> {noreply, wait_req(ReqID, State, fun({ok,Handle},State1) -> open2(ReqID,FileName,Handle,Mode,Async, From,State1); (Rep,State1) -> sync_reply(Rep, From, State1) end)} end;handle_call({opendir,Async,Path}, From, State) -> ReqID = State#state.req_id, ssh_xfer:opendir(?XF(State), ReqID, Path), make_reply(ReqID, Async, From, State);handle_call({readdir,Async,Handle}, From, State) -> ReqID = State#state.req_id, ssh_xfer:readdir(?XF(State), ReqID, Handle), make_reply(ReqID, Async, From, State);handle_call({close,_Async,Handle}, From, State) -> %% wait until all operations on handle are done case get_size(Handle, State) of undefined -> ReqID = State#state.req_id, ssh_xfer:close(?XF(State), ReqID, Handle), make_reply_post(ReqID, false, From, State, fun(Rep, State1) -> {Rep, erase_handle(Handle, State1)} end); _ -> case lseek_position(Handle, cur, State) of {ok,_} -> ReqID = State#state.req_id, ssh_xfer:close(?XF(State), ReqID, Handle), make_reply_post(ReqID, false, From, State, fun(Rep, State1) -> {Rep, erase_handle(Handle, State1)} end); Error -> {reply, Error, State} end end;handle_call({pread,Async,Handle,At,Length}, From, State) -> case lseek_position(Handle, At, State) of {ok,Offset} -> ReqID = State#state.req_id, ssh_xfer:read(?XF(State),ReqID,Handle,Offset,Length), %% To get multiple async read to work we must update the offset %% before the operation begins State1 = update_offset(Handle, Offset+Length, State), make_reply_post(ReqID,Async,From,State1, fun({ok,Data}, State2) -> case get_mode(Handle, State2) of binary -> {{ok,Data}, State2}; text -> {{ok,binary_to_list(Data)}, State2} end; (Rep, State2) -> {Rep, State2} end); Error -> {reply, Error, State} end;handle_call({read,Async,Handle,Length}, From, State) -> case lseek_position(Handle, cur, State) of {ok,Offset} -> ReqID = State#state.req_id, ssh_xfer:read(?XF(State),ReqID,Handle,Offset,Length), %% To get multiple async read to work we must update the offset %% before the operation begins State1 = update_offset(Handle, Offset+Length, State), make_reply_post(ReqID,Async,From,State1, fun({ok,Data}, State2) -> case get_mode(Handle, State2) of binary -> {{ok,Data}, State2}; text -> {{ok,binary_to_list(Data)}, State2} end; (Rep, State2) -> {Rep, State2} end); Error -> {reply, Error, State} end;handle_call({pwrite,Async,Handle,At,Data0}, From, State) -> case lseek_position(Handle, At, State) of {ok,Offset} -> Data = if binary(Data0) -> Data0; list(Data0) -> list_to_binary(Data0) end, ReqID = State#state.req_id, Size = size(Data), ssh_xfer:write(?XF(State),ReqID,Handle,Offset,Data), State1 = update_size(Handle, Offset+Size, State), make_reply(ReqID, Async, From, State1); Error -> {reply, Error, State} end;handle_call({write,Async,Handle,Data0}, From, State) -> case lseek_position(Handle, cur, State) of {ok,Offset} -> Data = if binary(Data0) -> Data0; list(Data0) -> list_to_binary(Data0) end, ReqID = State#state.req_id, Size = size(Data), ssh_xfer:write(?XF(State),ReqID,Handle,Offset,Data), State1 = update_offset(Handle, Offset+Size, State), make_reply(ReqID, Async, From, State1); Error -> {reply, Error, State} end;handle_call({position,Handle,At}, _From, State) -> %% We could make this auto sync when all request to Handle is done? case lseek_position(Handle, At, State) of {ok,Offset} -> {reply, {ok, Offset}, update_offset(Handle, Offset, State)}; Error -> {reply, Error, State} end;handle_call({rename,Async,FromFile,ToFile}, From, State) -> ReqID = State#state.req_id, ssh_xfer:rename(?XF(State),ReqID,FromFile,ToFile,[overwrite]), make_reply(ReqID, Async, From, State);handle_call({delete,Async,Name}, From, State) -> ReqID = State#state.req_id, ssh_xfer:remove(?XF(State), ReqID, Name), make_reply(ReqID, Async, From, State);handle_call({make_dir,Async,Name}, From, State) -> ReqID = State#state.req_id, ssh_xfer:mkdir(?XF(State), ReqID, Name, #ssh_xfer_attr{ type = directory }), make_reply(ReqID, Async, From, State);handle_call({del_dir,Async,Name}, From, State) -> ReqID = State#state.req_id, ssh_xfer:rmdir(?XF(State), ReqID, Name), make_reply(ReqID, Async, From, State);handle_call({real_path,Async,Name}, From, State) -> ReqID = State#state.req_id, ssh_xfer:realpath(?XF(State), ReqID, Name), make_reply(ReqID, Async, From, State);handle_call({read_file_info,Async,Name}, From, State) -> ReqID = State#state.req_id, ssh_xfer:stat(?XF(State), ReqID, Name, all), make_reply(ReqID, Async, From, State);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?