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