1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2000-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%%%---------------------------------------------------------------------- 22%%% File : file_server.erl 23%%% Author : Raimo Niskanen <raimo@erix.ericsson.se> 24%%% Purpose : A simple file server 25%%% Created : 13 Oct 2000 by Raimo Niskanen <raimo@erix.ericsson.se> 26%%%---------------------------------------------------------------------- 27 28-module(file_server). 29 30-behaviour(gen_server). 31 32%% External exports 33-export([format_error/1]). 34-export([start/0, start_link/0, stop/0]). 35 36%% gen_server callbacks 37-export([init/1, handle_call/3, handle_cast/2, handle_info/2, 38 terminate/2, code_change/3]). 39 40-define(FILE_IO_SERVER_TABLE, file_io_servers). 41 42-define(FILE_SERVER, file_server_2). % Registered name 43-define(FILE_IO_SERVER, file_io_server). % Module 44-define(PRIM_FILE, prim_file). % Module 45 46%%%---------------------------------------------------------------------- 47%%% API 48%%%---------------------------------------------------------------------- 49format_error({_Line, ?MODULE, Reason}) -> 50 io_lib:format("~w", [Reason]); 51format_error({_Line, Mod, Reason}) -> 52 Mod:format_error(Reason); 53format_error(ErrorId) -> 54 erl_posix_msg:message(ErrorId). 55 56start() -> do_start(start). 57start_link() -> do_start(start_link). 58 59stop() -> 60 gen_server:call(?FILE_SERVER, stop, infinity). 61 62%%%---------------------------------------------------------------------- 63%%% Callback functions from gen_server 64%%%---------------------------------------------------------------------- 65 66-type state() :: term(). % Internal type 67 68%%---------------------------------------------------------------------- 69%% Func: init/1 70%% Returns: {ok, State} | 71%% {ok, State, Timeout} | 72%% ignore | 73%% {stop, Reason} 74%%---------------------------------------------------------------------- 75 76-spec init([]) -> {'ok', state()}. 77 78init([]) -> 79 process_flag(trap_exit, true), 80 ?FILE_IO_SERVER_TABLE = ets:new(?FILE_IO_SERVER_TABLE, [named_table]), 81 {ok, undefined}. 82 83%%---------------------------------------------------------------------- 84%% Func: handle_call/3 85%% Returns: {reply, Reply, State} | 86%% {reply, Reply, State, Timeout} | 87%% {noreply, State} | 88%% {noreply, State, Timeout} | 89%% {stop, Reason, Reply, State} | (terminate/2 is called) 90%% {stop, Reason, State} (terminate/2 is called) 91%%---------------------------------------------------------------------- 92 93-spec handle_call(term(), term(), state()) -> 94 {'noreply', state()} | 95 {'reply', 'eof' | 'ok' | {'error', term()} | {'ok', term()}, state()} | 96 {'stop', 'normal', 'stopped', state()}. 97 98handle_call({open, Name, ModeList}, {Pid, _Tag} = _From, State) 99 when is_list(ModeList) -> 100 Child = ?FILE_IO_SERVER:start_link(Pid, Name, ModeList), 101 case Child of 102 {ok, P} when is_pid(P) -> 103 ets:insert(?FILE_IO_SERVER_TABLE, {P, Name}); 104 _ -> 105 ok 106 end, 107 {reply, Child, State}; 108 109handle_call({open, _Name, _Mode}, _From, State) -> 110 {reply, {error, einval}, State}; 111 112handle_call({read_file, Name}, _From, State) -> 113 {reply, ?PRIM_FILE:read_file(Name), State}; 114 115handle_call({write_file, Name, Bin}, _From, State) -> 116 {reply, ?PRIM_FILE:write_file(Name, Bin), State}; 117 118handle_call({set_cwd, Name}, _From, State) -> 119 {reply, ?PRIM_FILE:set_cwd(Name), State}; 120 121handle_call({delete, Name}, _From, State) -> 122 {reply, ?PRIM_FILE:delete(Name), State}; 123 124handle_call({rename, Fr, To}, _From, State) -> 125 {reply, ?PRIM_FILE:rename(Fr, To), State}; 126 127handle_call({make_dir, Name}, _From, State) -> 128 {reply, ?PRIM_FILE:make_dir(Name), State}; 129 130handle_call({del_dir, Name}, _From, State) -> 131 {reply, ?PRIM_FILE:del_dir(Name), State}; 132 133handle_call({list_dir, Name}, _From, State) -> 134 {reply, ?PRIM_FILE:list_dir(Name), State}; 135handle_call({list_dir_all, Name}, _From, State) -> 136 {reply, ?PRIM_FILE:list_dir_all(Name), State}; 137 138handle_call(get_cwd, _From, State) -> 139 {reply, ?PRIM_FILE:get_cwd(), State}; 140handle_call({get_cwd}, _From, State) -> 141 {reply, ?PRIM_FILE:get_cwd(), State}; 142handle_call({get_cwd, Name}, _From, State) -> 143 {reply, ?PRIM_FILE:get_cwd(Name), State}; 144 145handle_call({read_file_info, Name}, _From, State) -> 146 {reply, ?PRIM_FILE:read_file_info(Name), State}; 147 148handle_call({read_file_info, Name, Opts}, _From, State) -> 149 {reply, ?PRIM_FILE:read_file_info(Name, Opts), State}; 150 151handle_call({altname, Name}, _From, State) -> 152 {reply, ?PRIM_FILE:altname(Name), State}; 153 154handle_call({write_file_info, Name, Info}, _From, State) -> 155 {reply, ?PRIM_FILE:write_file_info(Name, Info), State}; 156 157handle_call({write_file_info, Name, Info, Opts}, _From, State) -> 158 {reply, ?PRIM_FILE:write_file_info(Name, Info, Opts), State}; 159 160handle_call({read_link_info, Name}, _From, State) -> 161 {reply, ?PRIM_FILE:read_link_info(Name), State}; 162 163handle_call({read_link_info, Name, Opts}, _From, State) -> 164 {reply, ?PRIM_FILE:read_link_info(Name, Opts), State}; 165 166handle_call({read_link, Name}, _From, State) -> 167 {reply, ?PRIM_FILE:read_link(Name), State}; 168handle_call({read_link_all, Name}, _From, State) -> 169 {reply, ?PRIM_FILE:read_link_all(Name), State}; 170 171handle_call({make_link, Old, New}, _From, State) -> 172 {reply, ?PRIM_FILE:make_link(Old, New), State}; 173 174handle_call({make_symlink, Old, New}, _From, State) -> 175 {reply, ?PRIM_FILE:make_symlink(Old, New), State}; 176 177handle_call({copy, SourceName, SourceOpts, DestName, DestOpts, Length}, 178 _From, State) -> 179 Reply = 180 case ?PRIM_FILE:open(SourceName, [read, binary | SourceOpts]) of 181 {ok, Source} -> 182 SourceReply = 183 case ?PRIM_FILE:open(DestName, 184 [write, binary | DestOpts]) of 185 {ok, Dest} -> 186 DestReply = 187 ?PRIM_FILE:copy(Source, Dest, Length), 188 ?PRIM_FILE:close(Dest), 189 DestReply; 190 {error, _} = Error -> 191 Error 192 end, 193 ?PRIM_FILE:close(Source), 194 SourceReply; 195 {error, _} = Error -> 196 Error 197 end, 198 {reply, Reply, State}; 199 200handle_call(stop, _From, State) -> 201 {stop, normal, stopped, State}; 202 203handle_call(Request, From, State) -> 204 error_logger:error_msg("handle_call(~tp, ~tp, _)", [Request, From]), 205 {noreply, State}. 206 207%%---------------------------------------------------------------------- 208%% Func: handle_cast/2 209%% Returns: {noreply, State} | 210%% {noreply, State, Timeout} | 211%% {stop, Reason, State} (terminate/2 is called) 212%%---------------------------------------------------------------------- 213 214-spec handle_cast(term(), state()) -> {'noreply', state()}. 215 216handle_cast(Msg, State) -> 217 error_logger:error_msg("handle_cast(~tp, _)", [Msg]), 218 {noreply, State}. 219 220%%---------------------------------------------------------------------- 221%% Func: handle_info/2 222%% Returns: {noreply, State} | 223%% {noreply, State, Timeout} | 224%% {stop, Reason, State} (terminate/2 is called) 225%%---------------------------------------------------------------------- 226 227-spec handle_info(term(), state()) -> 228 {'noreply', state()}. 229 230handle_info({'EXIT', Pid, _Reason}, State) when is_pid(Pid) -> 231 ets:delete(?FILE_IO_SERVER_TABLE, Pid), 232 {noreply, State}; 233 234handle_info(Info, State) -> 235 error_logger:error_msg("handle_Info(~tp, _)", [Info]), 236 {noreply, State}. 237 238%%---------------------------------------------------------------------- 239%% Func: terminate/2 240%% Purpose: Shutdown the server 241%% Returns: any (ignored by gen_server) 242%%---------------------------------------------------------------------- 243 244-spec terminate(term(), state()) -> 'ok'. 245 246terminate(_Reason, _State) -> 247 ok. 248 249%%---------------------------------------------------------------------- 250%% Func: code_change/3 251%% Purpose: Convert process state when code is changed 252%% Returns: {ok, NewState} 253%%---------------------------------------------------------------------- 254 255-spec code_change(term(), state(), term()) -> {'ok', state()}. 256 257code_change(_OldVsn, State, _Extra) -> 258 {ok, State}. 259 260%%%---------------------------------------------------------------------- 261%%% Internal functions 262%%%---------------------------------------------------------------------- 263 264%%% The basic file server and start-up. 265%%% 266%%% The file server just handles the open command/message and acts as a 267%%% router for messages between the port and the file processes. If a 268%%% file process terminates we close the associated file. 269 270%% Start = start | start_link 271do_start(Start) -> 272 case init:get_argument(master) of 273 error -> 274 gen_server:Start({local,?FILE_SERVER}, ?MODULE, [], []); 275 {ok, [[Node]]} -> 276 do_start(Start, list_to_atom(Node), ?FILE_SERVER); 277 X -> 278 {error, {get_argument, master, X}} 279 end. 280 281%% Should mimic gen_server:Start 282do_start(Start, Node, Name) -> 283 case rpc:call(Node, erlang, whereis, [Name]) of 284 Filer when is_pid(Filer); Filer =:= undefined -> 285 case catch do_start_slave(Start, Filer, Name) of 286 {'EXIT', Reason} -> 287 {error, Reason}; 288 Result -> 289 Result 290 end; 291 Other -> 292 {error, {no_master, Other}} 293 end. 294 295%% May exit upon failure, return {ok, SlavePid} if all well. 296do_start_slave(start_link, Filer, Name) -> 297 Self = self(), 298 Token = make_ref(), 299 Slave = spawn_link(fun() -> relay_start(Self, Token, Filer, Name) end), 300 receive 301 {started, Token} -> 302 {ok, Slave} 303 end; 304do_start_slave(start, Filer, Name) -> 305 Self = self(), 306 Token = make_ref(), 307 Slave = spawn(fun() -> relay_start(Self, Token, Filer, Name) end), 308 SlaveMonitor = erlang:monitor(process, Slave), 309 receive 310 {started, Token} -> 311 erlang:demonitor(SlaveMonitor, [flush]), 312 {ok, Slave}; 313 {'DOWN', SlaveMonitor, _, _, Reason} -> 314 exit(Reason) 315 end. 316 317%% We have the relay process file internal. 318%% We do not need to load slave as a mandatory module 319%% during system startup. 320 321relay_start(Parent, Token, Filer, Name) when is_pid(Filer) -> 322 case catch register(Name, self()) of 323 true -> 324 ok; 325 _ -> 326 exit({already_started, whereis(Name)}) 327 end, 328 %% This will fail towards an R5 node or older, Filer is a pid() 329 FilerMonitor = erlang:monitor(process, Filer), 330 process_flag(trap_exit, true), 331 Parent ! {started, Token}, 332 relay_loop(Parent, Filer, FilerMonitor); 333relay_start(Parent, Token, undefined, _Name) -> 334 %% Dummy process to keep kernel supervisor happy 335 process_flag(trap_exit, true), 336 Parent ! {started, Token}, 337 receive 338 {'EXIT', Parent, Reason} -> 339 exit(Reason) 340 end. 341 342relay_loop(Parent, Filer, FilerMonitor) -> 343 receive 344 {'DOWN', FilerMonitor, _, _, Reason} -> 345 exit(Reason); 346 {'EXIT', Parent, Reason} -> 347 exit(Reason); 348 Msg -> 349 Filer ! Msg 350 end, 351 relay_loop(Parent, Filer, FilerMonitor). 352