PageRenderTime 122ms CodeModel.GetById 3ms app.highlight 106ms RepoModel.GetById 1ms app.codeStats 0ms

/erts/preloaded/src/erl_prim_loader.erl

https://github.com/cobusc/otp
Erlang | 1572 lines | 1242 code | 163 blank | 167 comment | 3 complexity | b24d29bbc04cb861d6741d3dd45fbe05 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1%%
   2%% %CopyrightBegin%
   3%%
   4%% Copyright Ericsson AB 1996-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%% A primary loader, provides two different methods to fetch a file:
  22%% efile and inet. The efile method is simple communication with a
  23%% port program.
  24%%
  25%% The distribution loading was removed and replaced with
  26%% inet loading
  27%%
  28%% The start_it/4 function initializes a record with callback 
  29%% functions used to handle the interface functions.
  30%%
  31
  32-module(erl_prim_loader).
  33
  34%% If the macro DEBUG is defined during compilation, 
  35%% debug printouts are done through erlang:display/1.
  36%% Activate this feature by starting the compiler 
  37%% with> erlc -DDEBUG ... 
  38%% or by> setenv ERL_COMPILER_FLAGS DEBUG 
  39%% before running make (in the OTP make system)
  40%% (the example is for tcsh)
  41
  42-include("inet_boot.hrl").
  43
  44%% Public
  45-export([start/0, set_path/1, get_path/0, get_file/1,
  46         list_dir/1, read_file_info/1, read_link_info/1, get_cwd/0, get_cwd/1]).
  47
  48%% Used by erl_boot_server
  49-export([prim_init/0, prim_get_file/2, prim_list_dir/2,
  50         prim_read_file_info/3, prim_get_cwd/2]).
  51
  52%% Used by escript and code
  53-export([set_primary_archive/4]).
  54
  55%% Used by test suites
  56-export([purge_archive_cache/0]).
  57
  58%% Used by init and the code server.
  59-export([get_modules/2,get_modules/3, is_basename/1]).
  60
  61-include_lib("kernel/include/file.hrl").
  62
  63-type host() :: atom().
  64
  65-record(prim_state, {debug :: boolean(),
  66		     primary_archive}).
  67-type prim_state() :: #prim_state{}.
  68
  69-record(state, 
  70        {loader            :: 'efile' | 'inet',
  71         hosts = []        :: [host()], % hosts list (to boot from)
  72         data              :: 'noport' | port(), % data port etc
  73         timeout           :: timeout(),	 % idle timeout
  74         prim_state        :: prim_state()}).    % state for efile code loader
  75
  76-define(EFILE_IDLE_TIMEOUT, (6*60*1000)).	%purge archives
  77-define(INET_IDLE_TIMEOUT, (60*1000)). 		%tear down connection timeout
  78
  79%% Defines for inet as prim_loader
  80-define(INET_FAMILY, inet).
  81-define(INET_ADDRESS, {0,0,0,0}).
  82
  83-ifdef(DEBUG).
  84-define(dbg(Tag, Data), erlang:display({Tag,Data})).
  85-else.
  86-define(dbg(Tag, Data), true).
  87-endif.
  88
  89-define(SAFE2(Expr, State), 
  90        fun() ->
  91                case catch Expr of
  92                    {'EXIT',XXXReason} -> {{error,XXXReason}, State};
  93                    XXXRes -> XXXRes
  94                end
  95        end()).
  96
  97debug(#prim_state{debug = Deb}, Term) ->
  98    case Deb of
  99        false -> ok;
 100        true  -> erlang:display(Term)
 101    end.
 102
 103%%% --------------------------------------------------------
 104%%% Interface Functions. 
 105%%% --------------------------------------------------------
 106
 107-spec start() ->
 108	    {'ok', Pid} | {'error', What} when
 109      Pid :: pid(),
 110      What :: term().
 111start() ->
 112    Self = self(),
 113    Pid = spawn_link(fun() -> start_it(Self) end),
 114    receive
 115        {Pid,ok} ->
 116            {ok,Pid};
 117        {'EXIT',Pid,Reason} ->
 118            {error,Reason}
 119    end.
 120
 121start_it(Parent) ->
 122    process_flag(trap_exit, true),
 123    register(erl_prim_loader, self()),
 124    Loader = case init:get_argument(loader) of
 125		 {ok,[[Loader0]]} ->
 126		     Loader0;
 127		 error ->
 128		     "efile"
 129	     end,
 130    case Loader of
 131	"efile" -> start_efile(Parent);
 132	"inet" -> start_inet(Parent)
 133    end.
 134
 135%% Hosts must be a list of form ['1.2.3.4' ...]
 136start_inet(Parent) ->
 137    Hosts = case init:get_argument(hosts) of
 138		{ok,[Hosts0]} -> Hosts0;
 139		_ -> []
 140	    end,
 141    AL = ipv4_list(Hosts),
 142    ?dbg(addresses, AL),
 143    {ok,Tcp} = find_master(AL),
 144    init_ack(Parent),
 145    PS = prim_init(),
 146    State = #state {loader = inet,
 147                    hosts = AL,
 148                    data = Tcp,
 149                    timeout = ?INET_IDLE_TIMEOUT,
 150                    prim_state = PS},
 151    loop(State, Parent, []).
 152
 153start_efile(Parent) ->
 154    %% Check that we started in a valid directory.
 155    case prim_file:get_cwd() of
 156	{error, _} ->
 157	    %% At this point in the startup, we have no error_logger at all.
 158	    Report = "Invalid current directory or invalid filename "
 159		"mode: loader cannot read current directory\n",
 160	    erlang:display(Report),
 161	    exit({error, invalid_current_directory});
 162	_ ->
 163	    init_ack(Parent)
 164    end,
 165    PS = prim_init(),
 166    State = #state {loader = efile,
 167                    data = noport,
 168                    timeout = ?EFILE_IDLE_TIMEOUT,
 169                    prim_state = PS},
 170    loop(State, Parent, []).
 171
 172init_ack(Pid) ->
 173    Pid ! {self(),ok},
 174    ok.
 175
 176-spec set_path(Path) -> 'ok' when
 177      Path :: [Dir :: string()].
 178set_path(Paths) when is_list(Paths) ->
 179    request({set_path,Paths}).
 180
 181-spec get_path() -> {'ok', Path} when
 182      Path :: [Dir :: string()].
 183get_path() ->
 184    request({get_path,[]}).
 185
 186-spec get_file(Filename) -> {'ok', Bin, FullName} | 'error' when
 187      Filename :: atom() | string(),
 188      Bin :: binary(),
 189      FullName :: string().
 190get_file(File) when is_atom(File) ->
 191    get_file(atom_to_list(File));
 192get_file(File) ->
 193    check_file_result(get_file, File, request({get_file,File})).
 194
 195-spec list_dir(Dir) -> {'ok', Filenames} | 'error' when
 196      Dir :: string(),
 197      Filenames :: [Filename :: string()].
 198list_dir(Dir) ->
 199    check_file_result(list_dir, Dir, request({list_dir,Dir})).
 200
 201-spec read_file_info(Filename) -> {'ok', FileInfo} | 'error' when
 202      Filename :: string(),
 203      FileInfo :: file:file_info().
 204read_file_info(File) ->
 205    check_file_result(read_file_info, File, request({read_file_info,File})).
 206
 207-spec read_link_info(Filename) -> {'ok', FileInfo} | 'error' when
 208      Filename :: string(),
 209      FileInfo :: file:file_info().
 210read_link_info(File) ->
 211    check_file_result(read_link_info, File, request({read_link_info,File})).
 212
 213-spec get_cwd() -> {'ok', string()} | 'error'.
 214get_cwd() ->
 215    check_file_result(get_cwd, [], request({get_cwd,[]})).
 216
 217-spec get_cwd(string()) -> {'ok', string()} | 'error'.
 218get_cwd(Drive) ->
 219    check_file_result(get_cwd, Drive, request({get_cwd,[Drive]})).
 220
 221-spec set_primary_archive(File :: string() | 'undefined', 
 222			  ArchiveBin :: binary() | 'undefined',
 223			  FileInfo :: #file_info{} | 'undefined',
 224			  ParserFun :: fun())
 225			 -> {ok, [string()]} | {error,_}.
 226
 227set_primary_archive(undefined, undefined, undefined, ParserFun) ->
 228    request({set_primary_archive, undefined, undefined, undefined, ParserFun});
 229set_primary_archive(File, ArchiveBin, FileInfo, ParserFun)
 230  when is_list(File), is_binary(ArchiveBin), is_record(FileInfo, file_info) ->
 231    request({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun}).
 232
 233%% NOTE: Does not close the primary archive. Only closes all
 234%% open zip files kept in the cache. Should be called before an archive
 235%% file is to be removed (for example in the test suites).
 236
 237-spec purge_archive_cache() -> 'ok' | {'error', _}.
 238purge_archive_cache() ->
 239    request(purge_archive_cache).
 240
 241-spec get_modules([module()],
 242		  fun((atom(), string(), binary()) ->
 243			     {'ok',any()} | {'error',any()})) ->
 244			 {'ok',{[any()],[any()]}}.
 245
 246get_modules(Modules, Fun) ->
 247    request({get_modules,{Modules,Fun}}).
 248
 249-spec get_modules([module()],
 250		  fun((atom(), string(), binary()) ->
 251			     {'ok',any()} | {'error',any()}),
 252		  [string()]) ->
 253			 {'ok',{[any()],[any()]}}.
 254
 255get_modules(Modules, Fun, Path) ->
 256    request({get_modules,{Modules,Fun,Path}}).
 257
 258request(Req) ->
 259    Loader = whereis(erl_prim_loader),
 260    Loader ! {self(),Req},
 261    receive
 262        {Loader,Res} ->
 263            Res;
 264        {'EXIT',Loader,_What} ->
 265            error
 266    end.
 267
 268check_file_result(_, _, {error,enoent}) ->
 269    error;
 270check_file_result(_, _, {error,enotdir}) ->
 271    error;
 272check_file_result(_, _, {error,einval}) ->
 273    error;
 274check_file_result(Func, Target, {error,Reason}) ->   
 275    case (catch atom_to_list(Reason)) of
 276        {'EXIT',_} ->                           % exit trapped
 277            error;
 278        Errno ->                                % errno
 279            Process = case process_info(self(), registered_name) of
 280                          {registered_name,R} -> 
 281                              "Process: " ++ atom_to_list(R) ++ ".";
 282                          _ -> 
 283                              ""
 284                      end,
 285            TargetStr =
 286                if is_atom(Target) -> atom_to_list(Target);
 287                   is_list(Target) -> Target;
 288                   true -> []
 289                end,
 290            Report = 
 291                case TargetStr of
 292                    [] ->
 293                        "File operation error: " ++ Errno ++ ". " ++
 294                        "Function: " ++ atom_to_list(Func) ++ ". " ++ Process;
 295                    _ ->
 296                        "File operation error: " ++ Errno ++ ". " ++
 297                        "Target: " ++ TargetStr ++ ". " ++
 298                        "Function: " ++ atom_to_list(Func) ++ ". " ++ Process
 299                end,
 300            %% this is equal to calling error_logger:error_report/1 which
 301            %% we don't want to do from code_server during system boot
 302            logger ! {log,error,#{label=>{?MODULE,file_error},report=>Report},
 303                      #{pid=>self(),
 304                        gl=>group_leader(),
 305                        time=>erlang:monotonic_time(microsecond),
 306                        error_logger=>#{tag=>error_report,
 307                                        type=>std_error}}},
 308            error
 309    end;
 310check_file_result(_, _, Other) ->
 311    Other.
 312
 313%%% --------------------------------------------------------
 314%%% The main loop.
 315%%% --------------------------------------------------------
 316
 317loop(St0, Parent, Paths) ->
 318    receive
 319	{Pid,{set_path,NewPaths}} when is_pid(Pid) ->
 320	    Pid ! {self(),ok},
 321	    loop(St0, Parent, to_strs(NewPaths));
 322        {Pid,Req} when is_pid(Pid) ->
 323	    case handle_request(Req, Paths, St0) of
 324		ignore ->
 325		    ok;
 326		{Resp,#state{}=St1} ->
 327		    Pid ! {self(),Resp},
 328                    loop(St1, Parent, Paths);
 329		{_,State2,_} ->
 330                    exit({bad_state,Req,State2})
 331            end;
 332        {'EXIT',Parent,W} ->
 333            _ = handle_stop(St0),
 334            exit(W);
 335        {'EXIT',P,W} ->
 336            St1 = handle_exit(St0, P, W),
 337            loop(St1, Parent, Paths);
 338        _Message ->
 339            loop(St0, Parent, Paths)
 340    after St0#state.timeout ->
 341            St1 = handle_timeout(St0, Parent),
 342            loop(St1, Parent, Paths)
 343    end.
 344
 345handle_request(Req, Paths, St0) ->
 346    case Req of
 347	{get_path,_} ->
 348	    {{ok,Paths},St0};
 349	{get_file,File} ->
 350	    handle_get_file(St0, Paths, File);
 351	{get_modules,{Modules,Fun}} ->
 352	    handle_get_modules(St0, Modules, Fun, Paths);
 353	{get_modules,{Modules,Fun,ModPaths}} ->
 354	    handle_get_modules(St0, Modules, Fun, ModPaths);
 355	{list_dir,Dir} ->
 356	    handle_list_dir(St0, Dir);
 357	{read_file_info,File} ->
 358	    handle_read_file_info(St0, File);
 359	{read_link_info,File} ->
 360	    handle_read_link_info(St0, File);
 361	{get_cwd,[]} ->
 362	    handle_get_cwd(St0, []);
 363	{get_cwd,[_]=Args} ->
 364	    handle_get_cwd(St0, Args);
 365	{set_primary_archive,File,ArchiveBin,FileInfo,ParserFun} ->
 366	    handle_set_primary_archive(St0, File, ArchiveBin,
 367				       FileInfo, ParserFun);
 368	purge_archive_cache ->
 369	    handle_purge_archive_cache(St0);
 370	_ ->
 371	    ignore
 372    end.
 373
 374handle_get_file(State = #state{loader = efile}, Paths, File) ->
 375    ?SAFE2(efile_get_file_from_port(State, File, Paths), State);
 376handle_get_file(State = #state{loader = inet}, Paths, File) ->
 377    ?SAFE2(inet_get_file_from_port(State, File, Paths), State).
 378
 379handle_set_primary_archive(State= #state{loader = efile}, File, ArchiveBin, FileInfo, ParserFun) ->
 380    ?SAFE2(efile_set_primary_archive(State, File, ArchiveBin, FileInfo, ParserFun), State).
 381
 382handle_purge_archive_cache(#state{loader = efile}=State) ->
 383    prim_purge_cache(),
 384    {ok,State}.
 385
 386handle_list_dir(State = #state{loader = efile}, Dir) ->
 387    ?SAFE2(efile_list_dir(State, Dir), State);
 388handle_list_dir(State = #state{loader = inet}, Dir) ->
 389    ?SAFE2(inet_list_dir(State, Dir), State).
 390
 391handle_read_file_info(State = #state{loader = efile}, File) ->
 392    ?SAFE2(efile_read_file_info(State, File, true), State);
 393handle_read_file_info(State = #state{loader = inet}, File) ->
 394    ?SAFE2(inet_read_file_info(State, File), State).
 395
 396handle_read_link_info(State = #state{loader = efile}, File) ->
 397    ?SAFE2(efile_read_file_info(State, File, false), State);
 398handle_read_link_info(State = #state{loader = inet}, File) ->
 399    ?SAFE2(inet_read_link_info(State, File), State).
 400
 401handle_get_cwd(State = #state{loader = efile}, Drive) ->
 402    ?SAFE2(efile_get_cwd(State, Drive), State);
 403handle_get_cwd(State = #state{loader = inet}, Drive) ->
 404    ?SAFE2(inet_get_cwd(State, Drive), State).
 405    
 406handle_stop(State = #state{loader = efile}) ->
 407    State;
 408handle_stop(State = #state{loader = inet}) ->
 409    inet_stop_port(State).
 410
 411handle_exit(State = #state{loader = efile}, _Who, _Reason) ->
 412    State;
 413handle_exit(State = #state{loader = inet}, Who, Reason) ->
 414    inet_exit_port(State, Who, Reason).
 415
 416handle_timeout(State = #state{loader = efile}, Parent) ->
 417    efile_timeout_handler(State, Parent);
 418handle_timeout(State = #state{loader = inet}, Parent) ->
 419    inet_timeout_handler(State, Parent).
 420
 421%%% --------------------------------------------------------
 422%%% Functions which handle efile as prim_loader (default).
 423%%% --------------------------------------------------------
 424
 425
 426%% -> {{ok,BinFile,File},State} | {{error,Reason},State}
 427efile_get_file_from_port(State, File, Paths) ->
 428    case is_basename(File) of
 429        false ->                        % get absolute file name.
 430            efile_get_file_from_port2(State, File);
 431        true when Paths =:= [] ->       % get plain file name.
 432            efile_get_file_from_port2(State, File);
 433        true ->                         % use paths.
 434            efile_get_file_from_port3(State, File, Paths)
 435    end.
 436
 437efile_get_file_from_port2(#state{prim_state = PS} = State, File) ->
 438    {Res, PS2} = prim_get_file(PS, File),
 439    case Res of
 440        {error,port_died} ->
 441            exit('prim_load port died');
 442        {error,Reason} ->
 443            {{error,Reason},State#state{prim_state = PS2}};
 444        {ok,BinFile} ->
 445            {{ok,BinFile,File},State#state{prim_state = PS2}}
 446    end.
 447
 448efile_get_file_from_port3(State, File, [P | Paths]) ->
 449    case efile_get_file_from_port2(State, join(P, File)) of
 450        {{error,Reason},State1} when Reason =/= emfile ->
 451            case Paths of
 452                [] ->                           % return last error
 453                    {{error,Reason},State1};
 454                _ ->                            % try more paths
 455                    efile_get_file_from_port3(State1, File, Paths)
 456            end;
 457        Result ->
 458            Result
 459    end;
 460efile_get_file_from_port3(State, _File, []) ->
 461    {{error,enoent},State}.
 462
 463efile_set_primary_archive(#state{prim_state = PS} = State, File,
 464			  ArchiveBin, FileInfo, ParserFun) ->
 465    {Res, PS2} = prim_set_primary_archive(PS, File, ArchiveBin,
 466					  FileInfo, ParserFun),
 467    {Res,State#state{prim_state = PS2}}.
 468
 469efile_list_dir(#state{prim_state = PS} = State, Dir) ->
 470    {Res, PS2} = prim_list_dir(PS, Dir),
 471    {Res, State#state{prim_state = PS2}}.
 472
 473efile_read_file_info(#state{prim_state = PS} = State, File, FollowLinks) ->
 474    {Res, PS2} = prim_read_file_info(PS, File, FollowLinks),
 475    {Res, State#state{prim_state = PS2}}.
 476
 477efile_get_cwd(#state{prim_state = PS} = State, Drive) ->
 478    {Res, PS2} = prim_get_cwd(PS, Drive),
 479    {Res, State#state{prim_state = PS2}}.
 480
 481efile_timeout_handler(State, _Parent) ->
 482    prim_purge_cache(),
 483    State.
 484
 485%%% --------------------------------------------------------
 486%%% Read and process severals modules in parallel.
 487%%% --------------------------------------------------------
 488
 489handle_get_modules(#state{loader=efile}=St, Ms, Process, Paths) ->
 490    Primary = (St#state.prim_state)#prim_state.primary_archive,
 491    Res = case efile_any_archives(Paths, Primary) of
 492	      false ->
 493		  efile_get_mods_par(Ms, Process, Paths);
 494	      true ->
 495		  Get = fun efile_get_file_from_port/3,
 496		  gm_get_mods(St, Get, Ms, Process, Paths)
 497	  end,
 498    {Res,St};
 499handle_get_modules(#state{loader=inet}=St, Ms, Process, Paths) ->
 500    Get = fun inet_get_file_from_port/3,
 501    {gm_get_mods(St, Get, Ms, Process, Paths),St}.
 502
 503efile_get_mods_par(Ms, Process, Paths) ->
 504    Self = self(),
 505    Ref = make_ref(),
 506    GmSpawn = fun() ->
 507		      efile_gm_spawn({Self,Ref}, Ms, Process, Paths)
 508	      end,
 509    _ = spawn_link(GmSpawn),
 510    N = length(Ms),
 511    efile_gm_recv(N, Ref, [], []).
 512
 513efile_any_archives([H|T], Primary) ->
 514    case name_split(Primary, H) of
 515	{file,_} -> efile_any_archives(T, Primary);
 516	{archive,_,_} -> true
 517    end;
 518efile_any_archives([], _) ->
 519    false.
 520
 521efile_gm_recv(0, _Ref, Succ, Fail) ->
 522    {ok,{Succ,Fail}};
 523efile_gm_recv(N, Ref, Succ, Fail) ->
 524    receive
 525	{Ref,Mod,{ok,Res}} ->
 526	    efile_gm_recv(N-1, Ref, [{Mod,Res}|Succ], Fail);
 527	{Ref,Mod,{error,Res}} ->
 528	    efile_gm_recv(N-1, Ref, Succ, [{Mod,Res}|Fail])
 529    end.
 530
 531efile_gm_spawn(ParentRef, Ms, Process, Paths) ->
 532    efile_gm_spawn_1(0, Ms, ParentRef, Process, Paths).
 533
 534efile_gm_spawn_1(N, Ms, ParentRef, Process, Paths) when N >= 32 ->
 535    receive
 536	{'DOWN',_,process,_,_} ->
 537	    efile_gm_spawn_1(N-1, Ms, ParentRef, Process, Paths)
 538    end;
 539efile_gm_spawn_1(N, [M|Ms], ParentRef, Process, Paths) ->
 540    Get = fun() -> efile_gm_get(Paths, M, ParentRef, Process) end,
 541    _ = spawn_monitor(Get),
 542    efile_gm_spawn_1(N+1, Ms, ParentRef, Process, Paths);
 543efile_gm_spawn_1(_, [], _, _, _) ->
 544    ok.
 545
 546efile_gm_get(Paths, Mod, ParentRef, Process) ->
 547    File = atom_to_list(Mod) ++ init:objfile_extension(),
 548    efile_gm_get_1(Paths, File, Mod, ParentRef, Process).
 549
 550efile_gm_get_1([P|Ps], File0, Mod, {Parent,Ref}=PR, Process) ->
 551    File = join(P, File0),
 552    try prim_file:read_file(File) of
 553	{ok,Bin} ->
 554	    Res = gm_process(Mod, File, Bin, Process),
 555	    Parent ! {Ref,Mod,Res};
 556	Error ->
 557	    _ = check_file_result(get_modules, File, Error),
 558	    efile_gm_get_1(Ps, File0, Mod, PR, Process)
 559    catch
 560	_:Reason ->
 561	    Res = {error,{crash,Reason}},
 562	    Parent ! {Ref,Mod,Res}
 563    end;
 564efile_gm_get_1([], _, Mod, {Parent,Ref}, _Process) ->
 565    Parent ! {Ref,Mod,{error,enoent}}.
 566
 567gm_get_mods(St, Get, Ms, Process, Paths) ->
 568    gm_get_mods(St, Get, Ms, Process, Paths, [], []).
 569
 570gm_get_mods(St, Get, [M|Ms], Process, Paths, Succ, Fail) ->
 571    File = atom_to_list(M) ++ init:objfile_extension(),
 572    case gm_arch_get(St, Get, M, File, Paths, Process) of
 573	{ok,Res} ->
 574	    gm_get_mods(St, Get, Ms, Process, Paths,
 575			[{M,Res}|Succ], Fail);
 576	{error,Res} ->
 577	    gm_get_mods(St, Get, Ms, Process, Paths,
 578			Succ, [{M,Res}|Fail])
 579    end;
 580gm_get_mods(_St, _Get, [], _, _, Succ, Fail) ->
 581    {ok,{Succ,Fail}}.
 582
 583gm_arch_get(St, Get, Mod, File, Paths, Process) ->
 584    case Get(St, File, Paths) of
 585	{{error,_}=E,_} ->
 586	    E;
 587	{{ok,Bin,Full},_} ->
 588	    gm_process(Mod, Full, Bin, Process)
 589    end.
 590
 591gm_process(Mod, File, Bin, Process) ->
 592    try Process(Mod, File, Bin) of
 593	{ok,_}=Res -> Res;
 594	{error,_}=Res -> Res;
 595	Other -> {error,{bad_return,Other}}
 596    catch
 597	_:Error ->
 598	    {error,{crash,Error}}
 599    end.
 600
 601
 602%%% --------------------------------------------------------
 603%%% Functions which handle inet prim_loader
 604%%% --------------------------------------------------------
 605
 606%%
 607%% Connect to a boot master
 608%% return {ok, Socket}  TCP
 609%% AL is a list of boot servers (including broadcast addresses)
 610%%
 611find_master(AL) ->
 612    find_master(AL, ?EBOOT_RETRY, ?EBOOT_REQUEST_DELAY, ?EBOOT_SHORT_RETRY_SLEEP, 
 613               ?EBOOT_UNSUCCESSFUL_TRIES, ?EBOOT_LONG_RETRY_SLEEP).
 614
 615find_master(AL, Retry, ReqDelay, SReSleep, Tries, LReSleep) ->
 616    {ok,U} = ll_udp_open(0),
 617    find_master(U, Retry, AL, ReqDelay, SReSleep, [], Tries, LReSleep).
 618
 619%%
 620%% Master connect loop
 621%%
 622find_master(U, Retry, AddrL, ReqDelay, SReSleep, Ignore, Tries, LReSleep) ->
 623    case find_loop(U, Retry, AddrL, ReqDelay, SReSleep, Ignore, 
 624                   Tries, LReSleep) of
 625        [] ->   
 626            find_master(U, Retry, AddrL, ReqDelay, SReSleep, Ignore, 
 627                        Tries, LReSleep);
 628        Servers ->
 629            ?dbg(servers, Servers),
 630            case connect_master(Servers) of
 631                {ok, Socket} -> 
 632                    ll_close(U),
 633                    {ok, Socket};
 634                _Error ->
 635                    find_master(U, Retry, AddrL, ReqDelay, SReSleep, 
 636                                Servers ++ Ignore, Tries, LReSleep)
 637            end
 638    end.
 639
 640connect_master([{_Prio,IP,Port} | Servers]) ->
 641    case ll_tcp_connect(0, IP, Port) of
 642        {ok, S} -> {ok, S};
 643        _Error -> connect_master(Servers)
 644    end;
 645connect_master([]) ->
 646    {error, ebusy}.
 647
 648%%
 649%% Always return a list of boot servers or hang.
 650%%
 651find_loop(U, Retry, AL, ReqDelay, SReSleep, Ignore, Tries, LReSleep) ->
 652    case find_loop(U, Retry, AL, ReqDelay, []) of
 653        [] ->                                   % no response from any server
 654            erlang:display({erl_prim_loader,'no server found'}), % lifesign
 655            Tries1 =
 656		if Tries > 0 ->
 657			sleep(SReSleep),
 658			Tries - 1;
 659		   true ->
 660			sleep(LReSleep),
 661			0
 662		end,
 663            find_loop(U, Retry, AL, ReqDelay, SReSleep, Ignore, Tries1, LReSleep);
 664        Servers ->
 665            keysort(1, Servers -- Ignore)
 666    end.
 667
 668%% broadcast or send
 669find_loop(_U, 0, _AL, _Delay, Acc) ->
 670    Acc;
 671find_loop(U, Retry, AL, Delay, Acc) ->
 672    send_all(U, AL, [?EBOOT_REQUEST, erlang:system_info(version)]),
 673    find_collect(U, Retry-1, AL, Delay, Acc).
 674
 675find_collect(U,Retry,AL,Delay,Acc) ->
 676    receive
 677        {udp, U, IP, _Port, [$E,$B,$O,$O,$T,$R,Priority,T1,T0 | _Version]} ->
 678            Elem = {Priority,IP,T1*256+T0},
 679            ?dbg(got, Elem),
 680            case member(Elem, Acc) of
 681                false  -> find_collect(U, Retry, AL, Delay, [Elem | Acc]);
 682                true -> find_collect(U, Retry, AL, Delay, Acc)
 683            end;
 684        _Garbage ->
 685            ?dbg(collect_garbage, _Garbage),
 686            find_collect(U, Retry, AL, Delay, Acc)
 687    after Delay ->
 688            ?dbg(collected, Acc),
 689            case keymember(0, 1, Acc) of  %% got high priority server?
 690                true -> Acc;
 691                false -> find_loop(U, Retry, AL, Delay, Acc)
 692            end
 693    end.
 694
 695    
 696sleep(Time) ->
 697    receive after Time -> ok end.
 698
 699inet_exit_port(State, Port, _Reason) when State#state.data =:= Port ->
 700    State#state{data = noport, timeout = infinity};
 701inet_exit_port(State, _, _) ->
 702    State.
 703
 704
 705inet_timeout_handler(State, _Parent) ->
 706    Tcp = State#state.data,
 707    if is_port(Tcp) -> ll_close(Tcp);
 708       true -> ok
 709    end,
 710    State#state{timeout = infinity, data = noport}.
 711
 712%% -> {{ok,BinFile,Tag},State} | {{error,Reason},State}
 713inet_get_file_from_port(State, File, Paths) ->
 714    case is_basename(File) of
 715        false ->                        % get absolute file name.
 716            inet_send_and_rcv({get,File}, File, State);
 717        true when Paths =:= [] ->       % get plain file name.
 718            inet_send_and_rcv({get,File}, File, State);
 719        true ->                         % use paths.
 720            inet_get_file_from_port1(File, Paths, State)
 721    end.
 722
 723inet_get_file_from_port1(File, [P | Paths], State) ->
 724    File1 = join(P, File),
 725    case inet_send_and_rcv({get,File1}, File1, State) of
 726        {{error,Reason},State1} ->
 727            case Paths of
 728                [] ->                           % return last error
 729                    {{error,Reason},State1};
 730                _ ->                            % try more paths            
 731                    inet_get_file_from_port1(File, Paths, State1)
 732            end;
 733        Result -> Result
 734    end;
 735inet_get_file_from_port1(_File, [], State) ->
 736    {{error,file_not_found},State}.
 737
 738inet_send_and_rcv(Msg, Tag, State) when State#state.data =:= noport ->
 739    {ok,Tcp} = find_master(State#state.hosts),     %% reconnect
 740    inet_send_and_rcv(Msg, Tag, State#state{data = Tcp,
 741					    timeout = ?INET_IDLE_TIMEOUT});
 742inet_send_and_rcv(Msg, Tag, #state{data = Tcp, timeout = Timeout} = State) ->
 743    prim_inet:send(Tcp, term_to_binary(Msg)),
 744    receive
 745        {tcp,Tcp,BinMsg} ->
 746            case catch binary_to_term(BinMsg) of
 747                {get,{ok,BinFile}} ->
 748                    {{ok,BinFile,Tag},State};
 749                {_Cmd,Res={ok,_}} ->
 750                    {Res,State};
 751                {_Cmd,{error,Error}} ->
 752                    {{error,Error},State};
 753                {error,Error} ->
 754                    {{error,Error},State};
 755                {'EXIT',Error} ->
 756                    {{error,Error},State}
 757            end;
 758        {tcp_closed,Tcp} ->
 759            %% Ok we must reconnect
 760            inet_send_and_rcv(Msg, Tag, State#state{data = noport});
 761        {tcp_error,Tcp,_Reason} ->
 762            %% Ok we must reconnect
 763            inet_send_and_rcv(Msg, Tag, inet_stop_port(State));
 764        {'EXIT', Tcp, _} -> 
 765            %% Ok we must reconnect
 766            inet_send_and_rcv(Msg, Tag, State#state{data = noport})
 767    after Timeout ->
 768            %% Ok we must reconnect
 769            inet_send_and_rcv(Msg, Tag, inet_stop_port(State))
 770    end.
 771
 772%% -> {{ok,List},State} | {{error,Reason},State}
 773inet_list_dir(State, Dir) ->
 774    inet_send_and_rcv({list_dir,Dir}, list_dir, State).
 775
 776%% -> {{ok,Info},State} | {{error,Reason},State}
 777inet_read_file_info(State, File) ->
 778    inet_send_and_rcv({read_file_info,File}, read_file_info, State).
 779
 780%% -> {{ok,Info},State} | {{error,Reason},State}
 781inet_read_link_info(State, File) ->
 782    inet_send_and_rcv({read_link_info,File}, read_link_info, State).
 783
 784%% -> {{ok,Cwd},State} | {{error,Reason},State}
 785inet_get_cwd(State, []) ->
 786    inet_send_and_rcv(get_cwd, get_cwd, State);
 787inet_get_cwd(State, [Drive]) ->
 788    inet_send_and_rcv({get_cwd,Drive}, get_cwd, State).
 789
 790inet_stop_port(#state{data=Tcp}=State) ->
 791    prim_inet:close(Tcp),
 792    State#state{data=noport}.
 793
 794%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 795%%
 796%% Direct inet_drv access
 797%%
 798%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 799
 800tcp_options() ->
 801    [{mode,binary}, {packet,4}, {active, true}, {deliver,term}].
 802
 803tcp_timeout() -> 
 804    15000.
 805
 806%% options for udp  [list, {broadcast, true}, {active,true}]
 807udp_options() ->
 808    [{mode,list}, {active, true}, {deliver,term}, {broadcast,true}].
 809%%
 810%% INET version IPv4 addresses
 811%%
 812ll_tcp_connect(LocalPort, IP, RemotePort) ->
 813    case ll_open_set_bind(tcp, ?INET_FAMILY, stream, tcp_options(),
 814                          ?INET_ADDRESS, LocalPort) of
 815        {ok,S} ->
 816            case prim_inet:connect(S, IP, RemotePort, tcp_timeout()) of
 817                ok -> {ok, S};
 818                Error -> port_error(S, Error)
 819            end;
 820        Error -> Error
 821    end.
 822
 823%%
 824%% Open and initialize an udp port for broadcast
 825%%
 826ll_udp_open(P) ->
 827    ll_open_set_bind(udp, ?INET_FAMILY, dgram, udp_options(), ?INET_ADDRESS, P).
 828
 829
 830ll_open_set_bind(Protocol, Family, Type, SOpts, IP, Port) ->
 831    case prim_inet:open(Protocol, Family, Type) of
 832        {ok, S} ->
 833            case prim_inet:setopts(S, SOpts) of
 834                ok ->
 835                    case prim_inet:bind(S, IP, Port) of
 836                        {ok,_} ->
 837                            {ok, S};
 838                        Error -> port_error(S, Error)
 839                    end;
 840                Error -> port_error(S, Error)
 841            end;
 842        Error -> Error
 843    end.
 844                    
 845
 846ll_close(S) ->
 847    unlink(S),
 848    exit(S, kill).
 849
 850port_error(S, Error) ->
 851    unlink(S),
 852    prim_inet:close(S),
 853    Error.
 854    
 855%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 856
 857-spec prim_init() -> prim_state().
 858prim_init() ->
 859    Deb =
 860        case init:get_argument(loader_debug) of
 861            {ok, _} -> true;
 862            error -> false
 863        end,
 864    cache_new(#prim_state{debug = Deb}).
 865
 866prim_purge_cache() ->
 867    do_prim_purge_cache(get()).
 868
 869do_prim_purge_cache([{Key,Val}|T]) ->
 870    case Val of
 871	{Cache,_FI} ->
 872	    catch clear_cache(Key, Cache);
 873	_ ->
 874	    ok
 875    end,
 876    do_prim_purge_cache(T);
 877do_prim_purge_cache([]) ->
 878    ok.
 879
 880prim_set_primary_archive(PS, undefined, undefined, undefined, _ParserFun) ->
 881    debug(PS, {set_primary_archive, clean}),
 882    case PS#prim_state.primary_archive of
 883        undefined ->
 884            Res = {error, enoent},
 885            debug(PS, {return, Res}),
 886            {Res, PS};
 887        ArchiveFile ->
 888            {primary, PrimZip, _FI, _ParserFun2} = erase(ArchiveFile),
 889            ok = prim_zip:close(PrimZip),
 890            PS2 = PS#prim_state{primary_archive = undefined},
 891            Res = {ok, []},
 892            debug(PS2, {return, Res}),
 893            {Res, PS2}
 894    end;
 895
 896prim_set_primary_archive(PS, ArchiveFile0, ArchiveBin,
 897			 #file_info{} = FileInfo, ParserFun)
 898  when is_list(ArchiveFile0), is_binary(ArchiveBin) ->
 899    %% Try the archive file
 900    debug(PS, {set_primary_archive, ArchiveFile0, byte_size(ArchiveBin)}),
 901    ArchiveFile = real_path(absname(ArchiveFile0)),
 902    {Res3, PS3} =
 903        case PS#prim_state.primary_archive of
 904            undefined ->
 905                case load_prim_archive(ArchiveFile, ArchiveBin, FileInfo) of
 906                    {ok, PrimZip, FI, Ebins} ->
 907                        debug(PS, {set_primary_archive, Ebins}),
 908                        put(ArchiveFile, {primary, PrimZip, FI, ParserFun}),
 909                        {{ok, Ebins},
 910                         PS#prim_state{primary_archive = ArchiveFile}};
 911                    Error ->
 912                        debug(PS, {set_primary_archive, Error}),
 913                        {Error, PS}
 914                end;
 915            OldArchiveFile ->
 916                debug(PS, {set_primary_archive, clean}),
 917                {primary, PrimZip, _FI, _ParserFun} = erase(OldArchiveFile),
 918                ok = prim_zip:close(PrimZip),
 919                PS2 = PS#prim_state{primary_archive = undefined},
 920                prim_set_primary_archive(PS2, ArchiveFile, ArchiveBin,
 921					 FileInfo, ParserFun)
 922        end,
 923    debug(PS3, {return, Res3}),
 924    {Res3, PS3}.
 925
 926-spec prim_get_file(prim_state(), file:filename()) -> {_, prim_state()}.
 927prim_get_file(PS, File) ->
 928    debug(PS, {get_file, File}),
 929    {Res2, PS2} =
 930        case name_split(PS#prim_state.primary_archive, File) of
 931            {file, PrimFile} ->
 932                Res = prim_file:read_file(PrimFile),
 933                {Res, PS};
 934            {archive, ArchiveFile, FileInArchive} ->
 935                debug(PS, {archive_get_file, ArchiveFile, FileInArchive}),
 936                FileComponents = path_split(FileInArchive),
 937                Fun =
 938                    fun({Components, _GetInfo, GetBin}, Acc) ->
 939                            if
 940                                Components =:= FileComponents ->
 941                                    {false, {ok, GetBin()}};
 942                                true ->
 943                                    {true, Acc}
 944                            end
 945                    end,
 946                apply_archive(PS, Fun, {error, enoent}, ArchiveFile)
 947        end,
 948    debug(PS, {return, Res2}),
 949    {Res2, PS2}.    
 950
 951-spec prim_list_dir(prim_state(), file:filename()) ->
 952	 {{'ok', [file:filename()]}, prim_state()}
 953       | {{'error', term()}, prim_state()}.
 954prim_list_dir(PS, Dir) ->
 955    debug(PS, {list_dir, Dir}),
 956    {Res2, PS3} =
 957        case name_split(PS#prim_state.primary_archive, Dir) of
 958            {file, PrimDir} ->
 959                Res = prim_file:list_dir(PrimDir),
 960                {Res, PS};
 961            {archive, ArchiveFile, FileInArchive} ->
 962                debug(PS, {archive_list_dir, ArchiveFile, FileInArchive}),
 963                DirComponents = path_split(FileInArchive),
 964                Fun =
 965                    fun({Components, _GetInfo, _GetBin}, {Status, Names} = Acc) ->
 966                            case Components of
 967                                [RevName | DC] when DC =:= DirComponents ->
 968                                    case RevName of
 969                                        "" ->
 970                                            %% The listed directory
 971                                            {true, {ok, Names}};
 972                                        _ ->
 973                                            %% Plain file
 974                                            Name = reverse(RevName),
 975                                            {true, {Status, [Name | Names]}}
 976                                    end;
 977                                ["", RevName | DC] when DC =:= DirComponents ->
 978                                    %% Directory
 979                                    Name = reverse(RevName),
 980                                    {true, {Status, [Name | Names]}};
 981                                [RevName] when DirComponents =:= [""] ->
 982                                    %% File in top directory
 983                                    Name = reverse(RevName),
 984                                    {true, {ok, [Name | Names]}};
 985                                ["", RevName] when DirComponents =:= [""] ->
 986                                    %% Directory in top directory
 987                                    Name = reverse(RevName),
 988                                    {true, {ok, [Name | Names]}};
 989                                _ ->
 990                                    %% No match
 991                                    {true, Acc}
 992                            end
 993                    end,
 994                {{Status, Names}, PS2} =
 995                    apply_archive(PS, Fun, {error, []}, ArchiveFile),
 996                case Status of
 997                    ok    -> {{ok, Names}, PS2};
 998                    error -> {{error, enotdir}, PS2}
 999                end
1000        end,
1001    debug(PS, {return, Res2}),
1002    {Res2, PS3}.
1003
1004-spec prim_read_file_info(prim_state(), file:filename(), boolean()) ->
1005	{{'ok', #file_info{}}, prim_state()}
1006      | {{'error', term()}, prim_state()}.
1007prim_read_file_info(PS, File, FollowLinks) ->
1008    debug(PS, {read_file_info, File}),
1009    {Res2, PS2} =
1010        case name_split(PS#prim_state.primary_archive, File) of
1011            {file, PrimFile} ->
1012                case FollowLinks of
1013                    true -> {prim_file:read_file_info(PrimFile), PS};
1014                    false -> {prim_file:read_link_info(PrimFile), PS}
1015                end;
1016            {archive, ArchiveFile, []} ->
1017                %% Fake top directory
1018                debug(PS, {archive_read_file_info, ArchiveFile}),
1019                case prim_file:read_file_info(ArchiveFile) of
1020                    {ok, FI} ->
1021                        {{ok, FI#file_info{type = directory}}, PS};
1022                    Other ->
1023                        {Other, PS}
1024                end;
1025            {archive, ArchiveFile, FileInArchive} ->
1026                debug(PS, {archive_read_file_info, File}),
1027                FileComponents = path_split(FileInArchive),
1028                Fun =
1029                    fun({Components, GetInfo, _GetBin}, Acc)  ->
1030			    case Components of
1031				["" | F] when F =:= FileComponents ->
1032                                    %% Directory
1033                                    {false, {ok, GetInfo()}};
1034                                F when F =:= FileComponents ->
1035                                    %% Plain file
1036                                    {false, {ok, GetInfo()}};
1037                                _ ->
1038                                    %% No match
1039                                    {true, Acc}
1040                            end
1041                    end,
1042                apply_archive(PS, Fun, {error, enoent}, ArchiveFile)
1043        end,
1044    debug(PS2, {return, Res2}),
1045    {Res2, PS2}.
1046
1047-spec prim_get_cwd(prim_state(), [file:filename()]) ->
1048        {{'error', term()} | {'ok', _}, prim_state()}.
1049prim_get_cwd(PS, []) ->
1050    debug(PS, {get_cwd, []}),
1051    Res = prim_file:get_cwd(),
1052    debug(PS, {return, Res}),
1053    {Res, PS};
1054prim_get_cwd(PS, [Drive]) ->
1055    debug(PS, {get_cwd, Drive}),
1056    Res = prim_file:get_cwd(Drive),
1057    debug(PS, {return, Res}),
1058    {Res, PS}.
1059
1060%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1061
1062apply_archive(PS, Fun, Acc, Archive) ->
1063    case get(Archive) of
1064        undefined ->
1065	    case open_archive(Archive, Acc, Fun) of
1066		{ok, PrimZip, {Acc2, FI, _}} ->
1067		    debug(PS, {cache, ok}),
1068		    put(Archive, {{ok, PrimZip}, FI}),
1069		    {Acc2, PS};
1070		Error ->
1071		    debug(PS, {cache, Error}),
1072		    %% put(Archive, {Error, FI}),
1073		    {Error, PS}
1074	    end;
1075        {primary, PrimZip, FI, ParserFun} ->
1076	    case prim_file:read_file_info(Archive) of
1077                {ok, FI2} 
1078		  when FI#file_info.mtime =:= FI2#file_info.mtime ->
1079		    case foldl_archive(PrimZip, Acc, Fun) of
1080			{ok, _PrimZip2, Acc2} ->
1081			    {Acc2, PS};
1082			Error ->
1083			    debug(PS, {primary, Error}),
1084			    {Error, PS}
1085		    end;
1086		{ok, FI2} ->
1087		    ok = clear_cache(Archive, {ok, PrimZip}),
1088		    case load_prim_archive(Archive, FI2, ParserFun) of
1089			{ok, PrimZip2, FI3, _Ebins} ->
1090			    debug(PS, {cache, {update, Archive}}),
1091			    put(Archive, {primary, PrimZip2, FI3, ParserFun});
1092			Error2 ->
1093			    debug(PS, {cache, {clear, Error2}})
1094		    end,
1095		    apply_archive(PS, Fun, Acc, Archive);
1096		Error ->
1097		    debug(PS, {cache, {clear, Error}}),
1098		    ok = clear_cache(Archive, {ok, PrimZip}),
1099		    apply_archive(PS, Fun, Acc, Archive)
1100	    end;
1101        {Cache, FI} ->
1102            case prim_file:read_file_info(Archive) of
1103                {ok, FI2} 
1104		  when FI#file_info.mtime =:= FI2#file_info.mtime ->
1105                    case Cache of
1106                        {ok, PrimZip} ->
1107                            case foldl_archive(PrimZip, Acc, Fun) of
1108                                {ok, _PrimZip2, Acc2} ->
1109                                    {Acc2, PS};
1110                                Error ->
1111                                    debug(PS, {cache, {clear, Error}}),
1112                                    ok = clear_cache(Archive, Cache),
1113                                    debug(PS, {cache, Error}),
1114				    erase(Archive),
1115                                    %% put(Archive, {Error, FI}),
1116                                    {Error, PS}
1117                            end;
1118                        Error ->
1119                            debug(PS, {cache, Error}),
1120                            {Error, PS}
1121                    end;
1122                Error ->
1123                    debug(PS, {cache, {clear, Error}}),
1124                    ok = clear_cache(Archive, Cache),
1125                    apply_archive(PS, Fun, Acc, Archive)
1126            end
1127    end.
1128
1129open_archive(Archive, Acc, Fun) ->
1130    case prim_file:read_file_info(Archive) of
1131	{ok, FileInfo} ->
1132	    open_archive(Archive, FileInfo, Acc, Fun);
1133	{error, Reason} ->
1134	    {error, Reason}
1135    end.
1136
1137%% Open the given archive and iterate through all files with an own
1138%% wrapper fun in order to identify each file as a component list as
1139%% returned from path_split/1.
1140%%
1141%% In the archive (zip) file, directory elements might or might not be
1142%% present. To ensure consistency, a directory element is added if it
1143%% does not already exist (ensure_virtual_dirs/6). NOTE that there will
1144%% be no such directory element for the top directory of the archive.
1145open_archive(Archive, FileInfo, Acc, Fun) ->
1146    FakeFI = FileInfo#file_info{type = directory},
1147    Wrapper =
1148	fun({N, GI, GB}, {A, I, Dirs}) ->
1149		Components = path_split(N),
1150		Dirs2 =
1151		    case Components of
1152			["" | Dir] ->
1153			    %% This is a directory
1154			    [Dir | Dirs];
1155			_ ->
1156			    %% This is a regular file
1157			    Dirs
1158		    end,
1159		{Includes, Dirs3, A2} =
1160		    ensure_virtual_dirs(Components, Fun, FakeFI,
1161					[{true, Components}], Dirs2, A),
1162		{_Continue, A3} = Fun({Components, GI, GB}, A2),
1163		{true, Includes, {A3, I, Dirs3}}
1164	end,
1165    prim_zip:open(Wrapper, {Acc, FakeFI, []}, Archive).
1166
1167ensure_virtual_dirs(Components, Fun, FakeFI, Includes, Dirs, Acc) ->
1168    case Components of
1169	[_] ->
1170	    %% Don't add virtual dir for top directory
1171	    {Includes, Dirs, Acc};
1172	[_ | Dir] ->
1173	    case lists:member(Dir, Dirs) of % BIF
1174		false ->
1175		    %% The directory does not yet exist - add it
1176		    GetInfo = fun() -> FakeFI end,
1177		    GetBin = fun() -> <<>> end,
1178		    VirtualDir = ["" | Dir],
1179		    Includes2 = [{true, VirtualDir, GetInfo, GetBin} | Includes],
1180		    Dirs2 = [Dir | Dirs],
1181
1182		    %% Recursively ensure dir elements on all levels
1183		    {I, F, Acc2} = ensure_virtual_dirs(Dir, Fun, FakeFI,
1184						       Includes2, Dirs2, Acc),
1185
1186		    {_Continue, Acc3} = Fun({VirtualDir, GetInfo, GetBin}, Acc2),
1187		    {I, F, Acc3};
1188		true ->
1189		    %% The directory element does already exist
1190		    %% Recursivly ensure dir elements on all levels
1191		    ensure_virtual_dirs(Dir,Fun,FakeFI,Includes,Dirs,Acc)
1192	    end
1193    end.
1194
1195foldl_archive(PrimZip, Acc, Fun) ->
1196    Wrapper =
1197        fun({Components, GI, GB}, A) ->
1198                %% Allow partial iteration at foldl
1199                {Continue, A2} = Fun({Components, GI, GB}, A),
1200                {Continue, true, A2}
1201        end,                        
1202    prim_zip:foldl(Wrapper, Acc, PrimZip).
1203
1204cache_new(PS) ->
1205    PS.
1206
1207clear_cache(Archive, Cache) ->
1208    erase(Archive),
1209    case Cache of
1210        {ok, PrimZip} ->
1211            prim_zip:close(PrimZip);
1212        {error, _} ->
1213            ok
1214    end.
1215
1216%%% --------------------------------------------------------
1217%%% Misc. functions.
1218%%% --------------------------------------------------------
1219
1220%%% Look for directory separators
1221is_basename(File) ->
1222    case deep_member($/, File) of
1223        true -> 
1224            false;
1225        false ->
1226            case erlang:system_info(os_type) of
1227                {win32, _} ->
1228                    case File of
1229                        [_,$: | _] ->
1230			    false;
1231                        _ -> 
1232			    not deep_member($\\, File)
1233                    end;
1234                _ ->
1235                    true
1236            end
1237    end.
1238
1239send_all(U, [IP | AL], Cmd) ->
1240    ?dbg(sendto, {U, IP, ?EBOOT_PORT, Cmd}),
1241    prim_inet:sendto(U, IP, ?EBOOT_PORT, Cmd),
1242    send_all(U, AL, Cmd);
1243send_all(_U, [], _) -> ok.
1244
1245join(P, F) ->
1246    P ++ "/" ++ F.
1247
1248member(X, [X|_]) -> true;
1249member(X, [_|Y]) -> member(X, Y);
1250member(_X, [])   -> false.
1251
1252deep_member(X, [X|_]) -> 
1253    true;
1254deep_member(X, [List | Y]) when is_list(List) ->
1255    deep_member(X, List) orelse deep_member(X, Y);
1256deep_member(X, [Atom | Y]) when is_atom(Atom) ->
1257    deep_member(X, atom_to_list(Atom)) orelse deep_member(X, Y);
1258deep_member(X, [_ | Y]) -> 
1259    deep_member(X, Y);
1260deep_member(_X, [])   ->
1261    false.
1262
1263keymember(X, I, [Y | _]) when element(I,Y) =:= X -> true;
1264keymember(X, I, [_ | T]) -> keymember(X, I, T);
1265keymember(_X, _I, []) -> false.
1266
1267keysort(I, L) -> keysort(I, L, []).
1268
1269keysort(I, [X | L], Ls) ->
1270    keysort(I, L, keyins(X, I, Ls));
1271keysort(_I, [], Ls) -> Ls.
1272
1273keyins(X, I, [Y | T]) when X < element(I,Y) -> [X,Y|T];
1274keyins(X, I, [Y | T]) -> [Y | keyins(X, I, T)];
1275keyins(X, _I, []) -> [X].
1276
1277to_strs([P|Paths]) when is_atom(P) ->
1278    [atom_to_list(P)|to_strs(Paths)];
1279to_strs([P|Paths]) when is_list(P) ->
1280    [P|to_strs(Paths)];
1281to_strs([_|Paths]) ->
1282    to_strs(Paths);
1283to_strs([]) ->
1284    [].
1285
1286reverse([] = L) ->
1287    L;
1288reverse([_] = L) ->
1289    L;
1290reverse([A, B]) ->
1291    [B, A];
1292reverse([A, B | L]) ->
1293    lists:reverse(L, [B, A]). % BIF
1294                        
1295%% Returns a reversed list of path components, each component itself a
1296%% reversed list (string), e.g.
1297%% /path/to/file -> ["elif","ot","htap",""]
1298%% /path/to/dir/ -> ["","rid","ot","htap",""]
1299%% Note the "" marking leading and trailing / (slash).
1300path_split(List) ->
1301   path_split(List, [], []).
1302
1303path_split([$/ | Tail], Path, Paths) ->
1304    path_split(Tail, [], [Path | Paths]);
1305path_split([Head | Tail], Path, Paths) ->
1306    path_split(Tail, [Head | Path], Paths);
1307path_split([], Path, Paths) ->
1308    [Path | Paths].
1309
1310%% The opposite of path_split/1
1311path_join(Paths) ->
1312    path_join(Paths,[]).
1313
1314path_join([""],Acc) ->
1315    Acc;
1316path_join([Path],Acc) ->
1317    reverse(Path) ++ Acc;
1318path_join([Path|Paths],Acc) ->
1319    path_join(Paths,"/" ++ reverse(Path) ++ Acc).
1320
1321name_split(undefined, File) ->
1322    %% Ignore primary archive
1323    RevExt = reverse(init:archive_extension()),
1324    case archive_split(File, RevExt, []) of
1325        no_split ->
1326            {file, File};
1327	Archive ->
1328	    Archive
1329    end;
1330name_split(ArchiveFile, File0) ->
1331    %% Look first in primary archive
1332    File = absname(File0),
1333    case string_match(real_path(File), ArchiveFile) of
1334        no_match ->
1335            %% Archive or plain file
1336            name_split(undefined, File);
1337        {match, FileInArchive} ->
1338            %% Primary archive
1339	    {archive, ArchiveFile, FileInArchive}
1340    end.
1341
1342string_match([Char | File], [Char | Archive]) ->
1343    string_match(File, Archive);
1344string_match([] = File, []) ->
1345    {match, File};
1346string_match([$/ | File], []) ->
1347    {match, File};
1348string_match(_File, _Archive) ->
1349    no_match.
1350
1351archive_split("/"++File, RevExt, Acc) ->
1352    case is_prefix(RevExt, Acc) of
1353	false ->
1354	    archive_split(File, RevExt, [$/|Acc]);
1355	true ->
1356	    ArchiveFile = absname(reverse(Acc)),
1357	    {archive, ArchiveFile, File}
1358    end;
1359archive_split([H|T], RevExt, Acc) ->
1360    archive_split(T, RevExt, [H|Acc]);
1361archive_split([], RevExt, Acc) ->
1362    case is_prefix(RevExt, Acc) of
1363	false ->
1364	    no_split;
1365	true ->
1366	    ArchiveFile = absname(reverse(Acc)),
1367	    {archive, ArchiveFile, []}
1368    end.
1369
1370is_prefix([H|T1], [H|T2]) -> is_prefix(T1, T2);
1371is_prefix([_|_], _) -> false;
1372is_prefix([], _ ) -> true.
1373
1374%% Parse list of ipv4 addresses 
1375ipv4_list([H | T]) ->
1376    case ipv4_address(H) of
1377        {ok,IP} -> [IP | ipv4_list(T)];
1378        _ -> ipv4_list(T)
1379    end;
1380ipv4_list([]) -> [].
1381    
1382%%
1383%% Parse Ipv4 address: d1.d2.d3.d4 (from inet_parse)
1384%%
1385%% Return {ok, IP} | {error, einval}
1386%%
1387ipv4_address(Cs) ->
1388    case catch ipv4_addr(Cs, []) of
1389        {'EXIT',_} -> {error,einval};
1390        Addr -> {ok,Addr}
1391    end.
1392
1393ipv4_addr([C | Cs], IP) when C >= $0, C =< $9 -> ipv4_addr(Cs, C-$0, IP).
1394
1395ipv4_addr([$.|Cs], N, IP) when N < 256 -> ipv4_addr(Cs, [N|IP]);
1396ipv4_addr([C|Cs], N, IP) when C >= $0, C =< $9 ->
1397    ipv4_addr(Cs, N*10 + (C-$0), IP);
1398ipv4_addr([], D, [C,B,A]) when D < 256 -> {A,B,C,D}.
1399
1400%% A simplified version of filename:absname/1
1401absname(Name) ->
1402    Name2 = normalize(Name, []),
1403    case pathtype(Name2) of
1404	absolute ->
1405	    Name2;
1406	relative ->
1407	    case prim_file:get_cwd() of
1408		{ok, Cwd} ->
1409		    Cwd ++ "/" ++ Name2;
1410		{error, _} ->
1411		    Name2
1412	    end;
1413	volumerelative ->
1414	    case prim_file:get_cwd() of
1415		{ok, Cwd} ->
1416		    absname_vr(Name2, Cwd);
1417		{error, _} ->
1418		    Name2
1419	    end
1420    end.
1421
1422%% Assumes normalized name
1423absname_vr([$/ | NameRest], [Drive, $\: | _]) ->
1424    %% Absolute path on current drive.
1425    [Drive, $\: | NameRest];
1426absname_vr([Drive, $\: | NameRest], [Drive, $\: | _] = Cwd) ->
1427    %% Relative to current directory on current drive.
1428    Cwd ++ "/" ++ NameRest;
1429absname_vr([Drive, $\: | NameRest], _) ->
1430    %% Relative to current directory on another drive.
1431    case prim_file:get_cwd([Drive, $\:]) of
1432	{ok, DriveCwd}  ->
1433	    DriveCwd ++ "/" ++ NameRest;
1434	{error, _} ->
1435	    [Drive, $\:, $/] ++ NameRest
1436    end.
1437
1438%% Assumes normalized name
1439pathtype(Name) when is_list(Name) -> 
1440    case erlang:system_info(os_type) of
1441	{unix, _}  -> 
1442	    unix_pathtype(Name);
1443	{win32, _} ->
1444	    win32_pathtype(Name)
1445    end.
1446
1447unix_pathtype(Name) ->
1448    case Name of
1449	[$/|_] ->
1450	    absolute;
1451	[List|Rest] when is_list(List) ->
1452	    unix_pathtype(List++Rest);
1453	[Atom|Rest] when is_atom(Atom) ->
1454	    atom_to_list(Atom)++Rest;
1455	_ ->
1456	    relative
1457    end.
1458
1459win32_pathtype(Name) ->
1460    case Name of
1461	[List|Rest] when is_list(List) ->
1462	    win32_pathtype(List++Rest);
1463	[Atom|Rest] when is_atom(Atom) ->
1464	    win32_pathtype(atom_to_list(Atom)++Rest);
1465	[Char, List | Rest] when is_list(List) ->
1466	    win32_pathtype([Char | List++Rest]);
1467	[$/, $/|_] -> 
1468	    absolute;
1469	[$/|_] -> 
1470	    volumerelative;
1471	[C1, C2, List | Rest] when is_list(List) ->
1472	    win32_pathtype([C1, C2|List ++ Rest]);
1473	[_Letter, $:, $/|_] -> 
1474	    absolute;
1475	[_Letter, $:|_] -> 
1476	    volumerelative;
1477	_ -> 
1478	    relative
1479    end.
1480
1481normalize(Name, Acc) ->
1482    case Name of
1483	[List | Rest] when is_list(List) ->
1484	    normalize(List ++ Rest, Acc);
1485	[Atom | Rest] when is_atom(Atom) ->
1486	    normalize(atom_to_list(Atom) ++ Rest, Acc);
1487	[$\\ | Chars] ->
1488	    case erlang:system_info(os_type) of
1489                {win32, _} ->
1490		    normalize(Chars, [$/ | Acc]);
1491		_ ->
1492		    normalize(Chars, [$\\ | Acc])
1493	    end;
1494	[Char | Chars] ->
1495	    normalize(Chars, [Char | Acc]);
1496	[] ->
1497	    reverse(Acc)
1498    end.
1499
1500%% Remove .. and . from the path, e.g.
1501%% /path/./to/this/../file -> /path/to/file
1502%% This includes resolving symlinks.
1503%%
1504%% This is done to ensure that paths are totally normalized before
1505%% comparing to find out if a file is inside the primary archive or
1506%% not.
1507real_path(Name) ->
1508    real_path(Name,reverse(path_split(Name)),[],[]).
1509
1510real_path(_Name,[],Acc,_Links) ->
1511    path_join(Acc);
1512real_path(Name,["."|Paths],Acc,Links) ->
1513    real_path(Name,Paths,Acc,Links);
1514real_path(Name,[".."|Paths],[""]=Acc,Links) ->
1515    %% /.. -> / (can't get higher than root)
1516    real_path(Name,Paths,Acc,Links);
1517real_path(Name,[".."|Paths],[Prev|Acc],Links) when Prev=/=".." ->
1518    real_path(Name,Paths,Acc,Links);
1519real_path(Name,[Path|Paths],Acc,Links) ->
1520    This = [Path|Acc],
1521    ThisFile = path_join(This),
1522    case lists:member(ThisFile,Links) of
1523	true -> % circular!!
1524	    Name;
1525	false ->
1526	    case prim_file:read_link(ThisFile) of
1527		{ok,Link} ->
1528		    case reverse(pat

Large files files are truncated, but you can click here to view the full file