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 + -
显示快捷键?