From af23ae42edcd582ec72e6c97540f82ea03bb36fe Mon Sep 17 00:00:00 2001
From: Siri Hansen <siri@erlang.org>
Date: Tue, 7 Aug 2012 17:23:26 +0200
Subject: Fix flattening of paths in erl_prim_loader

When correcting OTP-10071, a new error was introduced in
erl_prim_loader. In order to improve ability to detect if a file was
inside the primary archive, all paths were flattened - i.e. "."  and
".." were removed. This implementation had some faults, and it did not
take symlinks into account. This has been corrected.
---
 lib/stdlib/test/escript_SUITE.erl                  | 69 ++++++++++++++--------
 .../archive_script_file_access.erl                 | 29 +++++++--
 2 files changed, 69 insertions(+), 29 deletions(-)

(limited to 'lib')

diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl
index 7b03fdafe3..e76d0972f3 100644
--- a/lib/stdlib/test/escript_SUITE.erl
+++ b/lib/stdlib/test/escript_SUITE.erl
@@ -469,22 +469,23 @@ archive_script(Config) when is_list(Config) ->
 %% Test the correction of OTP-10071
 %% The errors identified are
 %%
-%% * If primary archive was named "xxx", then a file in the same
-%%   directory named "xxxyyy" would be interpreted as a file named yyy
-%%   inside the archive.
+%% a) If primary archive was named "xxx", then a file in the same
+%%    directory named "xxxyyy" would be interpreted as a file named yyy
+%%    inside the archive.
 %%
-%% * erl_prim_loader did not correctly create and normalize absolute
-%%   paths for primary archive and files inside it, so unless given
-%%   with exact same path files inside the archive would not be
-%%   found. E.g. if escript was started as ./xxx then "xxx/file" would
-%%   not be found since erl_prim_loader would try to match
-%%   /full/path/to/xxx with /full/path/to/./xxx. Same problem with
-%%   ../
+%% b) erl_prim_loader did not correctly create and normalize absolute
+%%    paths for primary archive and files inside it, so unless given
+%%    with exact same path files inside the archive would not be
+%%    found. E.g. if escript was started as ./xxx then "xxx/file"
+%%    would not be found since erl_prim_loader would try to match
+%%    /full/path/to/xxx with /full/path/to/./xxx. Same problem with
+%%    ../. Also, the use of symlinks in the path to the archive would
+%%    cause problems.
 %%
-%% * Depending on how the primary archive was built,
-%%   erl_prim_loader:list_dir/1 would sometimes return an empty string
-%%   inside the file list. This was a virtual element representing the
-%%   top directory of the archive. This shall not occur.
+%% c) Depending on how the primary archive was built,
+%%    erl_prim_loader:list_dir/1 would sometimes return an empty string
+%%    inside the file list. This was a virtual element representing the
+%%    top directory of the archive. This shall not occur.
 %%
 archive_script_file_access(Config) when is_list(Config) ->
     %% Copy the orig files to priv_dir
@@ -542,18 +543,22 @@ archive_script_file_access(Config) when is_list(Config) ->
     ok = escript:create(Script1,[shebang,{emu_args,Flags},{archive,Bin1}]),
     ok = file:change_mode(Script1,8#00744),
 
+    %% If supported, create a symlink to the script. This is used to
+    %% test error b) described above this test case.
+    SymlinkName1 = "symlink_to_"++ScriptName1,
+    Symlink1 = filename:join([PrivDir, SymlinkName1]),
+    file:make_symlink(ScriptName1,Symlink1), % will fail if not supported
+
     %% Also add a dummy file in the same directory with the same name
     %% as the script except is also has an extension. This used to
-    %% cause erl_prim_loader to believe it was a file inside the
-    %% script.
+    %% test error a) described above this test case.
     ok = file:write_file(Script1 ++ ".extension",
 			 <<"same name as script, but with extension">>),
 
     %% Change to script's directory and run it as "./<script_name>"
     ok = file:set_cwd(PrivDir),
-    do_run(PrivDir, "./" ++ ScriptName1,
-	   [<<"file_access:[]\n",
-	      "ExitCode:0">>]),
+    do_run(PrivDir, "./" ++ ScriptName1 ++ " " ++ ScriptName1,
+	   [<<"ExitCode:0">>]),
     ok = file:set_cwd(TopDir),
 
 
@@ -574,18 +579,34 @@ archive_script_file_access(Config) when is_list(Config) ->
 
     %% Also add a dummy file in the same directory with the same name
     %% as the script except is also has an extension. This used to
-    %% cause erl_prim_loader to believe it was a file inside the
-    %% script.
+    %% test error a) described above this test case.
     ok = file:write_file(Script2 ++ ".extension",
 			 <<"same name as script, but with extension">>),
 
+    %% If supported, create a symlink to the script. This is used to
+    %% test error b) described above this test case.
+    SymlinkName2 = "symlink_to_"++ScriptName2,
+    Symlink2 = filename:join([PrivDir, SymlinkName2]),
+    file:make_symlink(ScriptName2,Symlink2), % will fail if not supported
+
     %% Change to script's directory and run it as "./<script_name>"
     ok = file:set_cwd(PrivDir),
-    do_run(PrivDir, "./" ++ ScriptName2,
-	   [<<"file_access:[]\n",
-	      "ExitCode:0">>]),
+    do_run(PrivDir, "./" ++ ScriptName2 ++ " " ++ ScriptName2,
+	   [<<"ExitCode:0">>]),
+
+    %% 3. If symlinks are supported, run one of the scripts via a symlink.
+    %%
+    %% This is in order to test error b) described above this test case.
+    case file:read_link(Symlink2) of
+	{ok,_} ->
+	    do_run(PrivDir, "./" ++ SymlinkName2 ++ " " ++ ScriptName2,
+		   [<<"ExitCode:0">>]);
+	_ -> % not supported
+	    ok
+    end,
     ok = file:set_cwd(OldDir).
 
+
 compile_app(TopDir, AppName) ->
     AppDir = filename:join([TopDir, AppName]),
     SrcDir = filename:join([AppDir, "src"]),
diff --git a/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl b/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl
index 226a8675db..b03c8ba70d 100644
--- a/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl
+++ b/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl
@@ -23,12 +23,9 @@
 
 -include_lib("kernel/include/file.hrl").
 
-main(MainArgs) ->
-    io:format("file_access:~p\n", [MainArgs]),
-    ArchiveFile = escript:script_name(),
+main([RelArchiveFile]) ->
 
-    AbsArchiveFile = filename:absname(ArchiveFile),
-    RelArchiveFile = filename:basename(ArchiveFile),
+    AbsArchiveFile = filename:absname(RelArchiveFile),
     DotSlashArchiveFile = "./" ++ RelArchiveFile,
 
     Beam = atom_to_list(?MODULE) ++ ".beam",
@@ -39,6 +36,10 @@ main(MainArgs) ->
     AbsDir = filename:join(AbsArchiveFile,Dir),
     RelDir = filename:join(RelArchiveFile,Dir),
     DotSlashDir = filename:join(DotSlashArchiveFile,Dir),
+    SubDir = "subdir1",
+    AbsSubDir = filename:join(AbsDir,SubDir),
+    RelSubDir = filename:join(RelDir,SubDir),
+    DotSlashSubDir = filename:join(DotSlashDir,SubDir),
 
     {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile),
     {ok,List1} = erl_prim_loader:list_dir(RelArchiveFile),
@@ -48,8 +49,26 @@ main(MainArgs) ->
     {ok,List1} = erl_prim_loader:list_dir(filename:join([AbsDir,".."])),
     {ok,List1} = erl_prim_loader:list_dir(filename:join([RelDir,".."])),
     {ok,List1} = erl_prim_loader:list_dir(filename:join([DotSlashDir,".."])),
+    {ok,List1} = erl_prim_loader:list_dir(filename:join([AbsSubDir,"..",".."])),
+    {ok,List1} = erl_prim_loader:list_dir(filename:join([RelSubDir,"..",".."])),
+    {ok,List1} = erl_prim_loader:list_dir(filename:join([DotSlashSubDir,"..",".."])),
     false = lists:member([],List1),
 
+    %% If symlinks are supported on this platform...
+    RelSymlinkArchiveFile = "symlink_to_" ++ RelArchiveFile,
+    case file:read_link(RelSymlinkArchiveFile) of
+	{ok,_} ->
+	    DotSlashSymlinkArchiveFile = "./" ++ RelSymlinkArchiveFile,
+	    AbsSymlinkArchiveFile=filename:join(filename:dirname(AbsArchiveFile),
+						RelSymlinkArchiveFile),
+	    {ok,List1} = erl_prim_loader:list_dir(AbsSymlinkArchiveFile),
+	    {ok,List1} = erl_prim_loader:list_dir(RelSymlinkArchiveFile),
+	    {ok,List1} = erl_prim_loader:list_dir(DotSlashSymlinkArchiveFile);
+	_ -> % not supported
+	    ok
+    end,
+
+
     {ok,List2} = erl_prim_loader:list_dir(AbsDir),
     {ok,List2} = erl_prim_loader:list_dir(RelDir),
     {ok,List2} = erl_prim_loader:list_dir(DotSlashDir),
-- 
cgit v1.2.3