PageRenderTime 29ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/erts/preloaded/src/erl_prim_loader.erl

https://github.com/bsmr-erlang/otp
Erlang | 1573 lines | 1242 code | 163 blank | 168 comment | 3 complexity | 054333483ad6ac93bcdd243c680fe98a MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0

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

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