ssh_sftpd.erl

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

ERL
641
字号
    send_status(Status, ReqId, State);handle_op(?SSH_FXP_RENAME, ReqId,	  Bin = <<?UINT32(PLen), _:PLen/binary, ?UINT32(PLen2), 		 _:PLen2/binary>>, State = #state{xf = #ssh_xfer{vsn = 3}}) ->    handle_op(?SSH_FXP_RENAME, ReqId, <<Bin/binary, 0:32>>, State);handle_op(?SSH_FXP_RENAME, ReqId,	  <<?UINT32(PLen), BPath:PLen/binary, ?UINT32(PLen2), 	   BPath2:PLen2/binary,	   ?UINT32(Flags)>>, State = #state{file_handler = FileMod}) ->    Path = relate_file_name(BPath, State),    Path2 = relate_file_name(BPath2, State),    case Flags band ?SSH_FXP_RENAME_ATOMIC of	0 ->	    case Flags band ?SSH_FXP_RENAME_OVERWRITE of		0 ->		    case FileMod:read_link_info(Path2) of			{ok, _Info} ->			    ssh_xfer:xf_send_status(State#state.xf, ReqId,						    ?SSH_FX_FILE_ALREADY_EXISTS),			    State;			_ ->			    rename(Path, Path2, ReqId, State)		    end;		_ ->		    rename(Path, Path2, ReqId, State)	    end;	_ ->	    ssh_xfer:xf_send_status(State#state.xf, ReqId,				    ?SSH_FX_OP_UNSUPPORTED),	    State    end;handle_op(?SSH_FXP_SYMLINK, ReqId,	  <<?UINT32(PLen), BPath:PLen/binary, ?UINT32(PLen2),	   BPath2:PLen2/binary>>, State = #state{file_handler = FileMod}) ->    Path = relate_file_name(BPath, State),    Path2 = relate_file_name(BPath2, State),    Status = FileMod:make_symlink(Path2, Path),    send_status(Status, ReqId, State).%%--------------------------------------------------------------------%% Function: terminate(Reason, State) -> void()%% Description: This function is called by a gen_server when it is about to%% terminate. It should be the opposite of Module:init/1 and do any necessary%% cleaning up. When it returns, the gen_server terminates with Reason.%% The return value is ignored.%%--------------------------------------------------------------------terminate(_, #state{handles = Handles, file_handler = FileMod}) ->    CloseFun = fun({_, file, {_, Fd}}) ->		       FileMod:close(Fd);		  (_) ->		       ok	       end,    lists:foreach(CloseFun, Handles),    ok.%%--------------------------------------------------------------------%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}%% Description: Convert process state when code is changed%%--------------------------------------------------------------------code_change(_OldVsn, State, _Extra) ->    {ok, State}.%%--------------------------------------------------------------------%%% Internal functions%%--------------------------------------------------------------------new_handle([], H) ->    H;new_handle([{N, _} | Rest], H) when N > H ->    new_handle(Rest, N+1);new_handle([_ | Rest], H) ->    new_handle(Rest, H).add_handle(State, XF, ReqId, Type, DirFileTuple) ->    Handles = State#state.handles,    Handle = new_handle(Handles, 0),    ssh_xfer:xf_send_handle(XF, ReqId, integer_to_list(Handle)),    State#state{handles = [{Handle, Type, DirFileTuple} | Handles]}.    get_handle(Handles, BinHandle) ->    case (catch list_to_integer(binary_to_list(BinHandle))) of	I when integer(I) ->	    case lists:keysearch(I, 1, Handles) of		{value, T} -> T;		false -> error	    end;	_ ->	    error    end.%%% read_dir/5: read directory, send names, and return new stateread_dir(State = #state{file_handler = FileMod}, XF, ReqId, Handle, RelPath) ->    AbsPath = relate_file_name(RelPath, State),    ?dbg(true, "read_dir: AbsPath=~p\n", [AbsPath]),    case FileMod:list_dir(AbsPath) of	{ok, Files} ->	    NamesAndAttrs = get_attrs(AbsPath, Files, FileMod),	    ssh_xfer:xf_send_names(XF, ReqId, NamesAndAttrs),	    Handles = lists:keyreplace(Handle, 1,				       State#state.handles,				       {Handle, directory, {RelPath,eof}}),	    State#state{handles = Handles};	{error, Error} ->	    send_status({error, Error}, ReqId, State)    end.%%% get_attrs: get stat of each file and returnget_attrs(RelPath, Files, FileMod) ->    lists:map(fun(F) ->		      Path = filename:absname(F, RelPath),		      ?dbg(true, "get_attrs fun: F=~p\n", [F]),		      {ok, Info} = FileMod:read_link_info(Path),		      Attrs = ssh_sftp:info_to_attr(Info),		      {F, Attrs}	      end, Files).close_our_file({_,Fd}, FileMod) ->    FileMod:close(Fd).%%% stat: do the statstat(Vsn, ReqId, Data, State, F) when Vsn =< 3->    <<?UINT32(BLen), BPath:BLen/binary>> = Data,    stat(ReqId, binary_to_list(BPath), State, F);stat(Vsn, ReqId, Data, State, F) when Vsn >= 4->    <<?UINT32(BLen), BPath:BLen/binary, ?UINT32(_Flags)>> = Data,    stat(ReqId, binary_to_list(BPath), State, F).fstat(Vsn, ReqId, Data, State) when Vsn =< 3->    <<?UINT32(HLen), Handle:HLen/binary>> = Data,    fstat(ReqId, Handle, State);fstat(Vsn, ReqId, Data, State) when Vsn >= 4->    <<?UINT32(HLen), Handle:HLen/binary, ?UINT32(_Flags)>> = Data,    fstat(ReqId, Handle, State).fstat(ReqId, BinHandle, State) ->    case get_handle(State#state.handles, BinHandle) of	{_Handle, _Type, {Path, _}} ->	    stat(ReqId, Path, State, read_file_info);	_ ->	    ssh_xfer:xf_send_status(State#state.xf, ReqId, 				    ?SSH_FX_INVALID_HANDLE),	    State    end.stat(ReqId, RelPath, State = #state{file_handler = FileMod}, F) ->    AbsPath = relate_file_name(RelPath, State),    XF = State#state.xf,    ?dbg(false, "stat: AbsPath=~p\n", [AbsPath]),    case FileMod:F(AbsPath) of	{ok, FileInfo} ->	    ssh_xfer:xf_send_attr(XF, ReqId, ssh_sftp:info_to_attr(FileInfo)),	    State;	{error, E} ->	    send_status({error, E}, ReqId, State)    end,    State.decode_4_open_flag(create_new) ->    [write];decode_4_open_flag(create_truncate) ->    [write];decode_4_open_flag(truncate_existing) ->    [write];decode_4_open_flag(open_existing) ->    [read,write].decode_4_flags([OpenFlag | Flags]) ->    decode_4_flags(Flags, decode_4_open_flag(OpenFlag)).decode_4_flags([], Flags) ->    Flags;decode_4_flags([append_data|R], _Flags) ->    decode_4_flags(R, [append]);decode_4_flags([append_data_atomic|R], _Flags) ->    decode_4_flags(R, [append]);decode_4_flags([_|R], Flags) ->    decode_4_flags(R, Flags).open(Vsn, ReqId, Data, State) when Vsn =< 3 ->    <<?UINT32(BLen), BPath:BLen/binary, ?UINT32(PFlags),     _Attrs/binary>> = Data,    Path = binary_to_list(BPath),%%     ?dbg(true, "open: PFlags=~p\n", [PFlags]),    Flags = ssh_xfer:decode_open_flags(Vsn, PFlags) -- [creat, excl, trunc],%%     ?dbg(true, "open: F=~p\n", [F]),%%     Flags = F -- [trunc],%%     Flags = case lists:member(trunc, F) of%% 		true -> F -- [trunc];%% 		_ -> F%% 	    end,    ?dbg(true, "open: Flags=~p\n", [Flags]),    do_open(ReqId, State, Path, Flags);open(Vsn, ReqId, Data, State) when Vsn >= 4 ->    <<?UINT32(BLen), BPath:BLen/binary, ?UINT32(_Access),     ?UINT32(PFlags), _Attrs/binary>> = Data,    Path = binary_to_list(BPath),    Fl = ssh_xfer:decode_open_flags(Vsn, PFlags),    ?dbg(true, "open: Fl=~p\n", [Fl]),    Flags = decode_4_flags(Fl),    ?dbg(true, "open: Flags=~p\n", [Flags]),    do_open(ReqId, State, Path, Flags).do_open(ReqId, State = #state{file_handler = FileMod}, Path, Flags) ->    XF = State#state.xf,    F = [raw, 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 ->    case FileMod:open(Path, F) of	{ok, IoDevice} ->	    add_handle(State, XF, ReqId, file, {Path,IoDevice});	{error, Error} ->	    ssh_xfer:xf_send_status(State#state.xf, ReqId,				    ssh_xfer:encode_erlang_status(				      Error)),	    State	    %%end    end.%%% relate given filenames to our CWD (needed? seems we get abs. path%%% most times)relate_file_name(F, State) when binary(F) ->    relate_file_name(binary_to_list(F), State);relate_file_name(F, #state{cwd = CWD}) ->    F1 = filename:absname(F, CWD),        filename:join(fix_file_name(lists:reverse(filename:split(F1)), [])).%%% fix file just a little: a/b/.. -> a/b and a/. -> afix_file_name(["..", _ | Rest], Acc) ->    fix_file_name(Rest, Acc);fix_file_name(["." | Rest], Acc) ->    fix_file_name(Rest, Acc);fix_file_name([A | Rest], Acc) ->    fix_file_name(Rest, [A | Acc]);fix_file_name([], Acc) ->    Acc.    read_file(ReqId, IoDevice, Offset, Len,		  State = #state{file_handler = FileMod}) ->    case FileMod:position(IoDevice, {bof, Offset}) of	{ok, _NewPos} ->	    case FileMod:read(IoDevice, Len) of		{ok, Data} ->		    ssh_xfer:xf_send_data(State#state.xf, ReqId, Data),		    State;		{error, Error} ->		    send_status({error, Error}, ReqId, State);		eof ->		    send_status(eof, ReqId, State)	    end;	{error, Error} ->		    send_status({error, Error}, ReqId, State)    end.write_file(ReqId, IoDevice, Offset, Data, 	   State = #state{file_handler = FileMod}) ->    case FileMod:position(IoDevice, {bof, Offset}) of	{ok, _NewPos} ->	    Status = FileMod:write(IoDevice, Data),	    send_status(Status, ReqId, State);	{error, Error} ->	    send_status({error, Error}, ReqId, State)    end.get_status(ok) ->    ?SSH_FX_OK;get_status(eof) ->    ?SSH_FX_EOF;get_status({error,Error}) ->    ssh_xfer:encode_erlang_status(Error).send_status(Status, ReqId, State) ->    ssh_xfer:xf_send_status(State#state.xf, ReqId, get_status(Status)),    State.%%set_stat(<<>>, _Path, _State) ->    ok;set_stat(Attr, Path, State = #state{file_handler = FileMod}) ->    {DecodedAttr, _Rest} = 	ssh_xfer:decode_ATTR((State#state.xf)#ssh_xfer.vsn, Attr),    ?dbg(true, "set_stat DecodedAttr=~p\n", [DecodedAttr]),    Info = ssh_sftp:attr_to_info(DecodedAttr),    case FileMod:read_link_info(Path) of	{ok, OldInfo} ->	    NewInfo = set_file_info(Info, OldInfo),	    ?dbg(true, "set_stat Path=~p\nInfo=~p\nOldInfo=~p\nNewInfo=~p\n",		 [Path, Info, OldInfo, NewInfo]),	    FileMod:write_file_info(Path, NewInfo);	{error, Error} ->	    {error, Error}    end.set_file_info_sel(undefined, F) ->    F;set_file_info_sel(F, _) ->    F.set_file_info(#file_info{atime = Dst_atime, mtime = Dst_mtime, 			 ctime = Dst_ctime,			 mode = Dst_mode, uid = Dst_uid, gid = Dst_gid},	      #file_info{atime = Src_atime, mtime = Src_mtime, 			 ctime = Src_ctime,			 mode = Src_mode, uid = Src_uid, gid = Src_gid}) ->        #file_info{atime = set_file_info_sel(Dst_atime, Src_atime),	       mtime = set_file_info_sel(Dst_mtime, Src_mtime),	       ctime = set_file_info_sel(Dst_ctime, Src_ctime),	       mode = set_file_info_sel(Dst_mode, Src_mode),	       uid = set_file_info_sel(Dst_uid, Src_uid),	       gid = set_file_info_sel(Dst_gid, Src_gid)}.rename(Path, Path2, ReqId, State = #state{file_handler = FileMod}) ->    Status = FileMod:rename(Path, Path2),    send_status(Status, ReqId, State).

⌨️ 快捷键说明

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