From 07943b1b41e8d1ffdf8b9f770e623212581b12cd Mon Sep 17 00:00:00 2001 From: emtenet Date: Sun, 24 Apr 2016 15:01:09 +1000 Subject: Do not create a junction (soft link) for files When symlink_or_copy/2 cannot use file:make_symlink/2 on Windows due to the user lacking SeCreateSymbolicLinkPrivilege it tries a fall back. Detect the source type and use an appropriate fall back: * junction for directories and, * copy for files. Improve error detection in win32_make_junction/2 and make it repeatable when the target exists --- src/rlx_util.erl | 59 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/rlx_util.erl b/src/rlx_util.erl index f68c291..1893353 100644 --- a/src/rlx_util.erl +++ b/src/rlx_util.erl @@ -45,6 +45,8 @@ -define(DFLT_INTENSITY, high). -define(ONE_LEVEL_INDENT, " "). + +-include_lib("kernel/include/file.hrl"). %%============================================================================ %% types %%============================================================================ @@ -233,21 +235,62 @@ symlink_or_copy(Source, Target) -> ok; {error, eexist} -> {error, eexist}; - {error, _} = Error -> + {error, eperm} = Error -> + % We get eperm on Windows if we do not have + % SeCreateSymbolicLinkPrivilege + % Try the next alternative case os:type() of {win32, _} -> - S = unicode:characters_to_list(Source), - T = unicode:characters_to_list(Target), - win32_symlink(filename:nativename(S), filename:nativename(T)); + win32_make_junction_or_copy(Source, Target); _ -> Error - end + end; + {error, _} = Error -> + Error + end. + +win32_make_junction_or_copy(Source, Target) -> + case filelib:is_dir(Source) of + true -> + win32_make_junction(Source, Target); + _ -> + ec_file:copy(Source, Target) end. +win32_make_junction(Source, Target) -> + % The mklink will fail if the target already exists, check for that first + case file:read_link_info(Target) of + {error, enoent} -> + win32_make_junction_cmd(Source, Target); + {ok, #file_info{type = symlink}} -> + case file:read_link(Target) of + {ok, Source} -> + ok; + {ok, _} -> + ok = file:del_dir(Target), + win32_make_junction_cmd(Source, Target); + {error, Reason} -> + {error, {readlink, Reason}} + end; + {ok, #file_info{type = Type}} -> + {error, {mklink_cannot_replace_existing, Type, Target}}; + Error -> + Error + end. -win32_symlink(Source, Target) -> - os:cmd("cmd /c mklink /j " ++ Target ++ " " ++ Source), - ok. +win32_make_junction_cmd(Source, Target) -> + S = unicode:characters_to_list(Source), + T = unicode:characters_to_list(Target), + Cmd = "cmd /c mklink /j " ++ filename:nativename(T) ++ " " ++ filename:nativename(S), + case os:cmd(Cmd) of + "Junction created " ++ _ -> + ok; + [] -> + % When mklink fails it prints the error message to stderr which + % is not picked up by os:cmd() hence this case switch is for + % an empty message + {error, make_junction_failed} + end. %% @doc Returns the color intensity, we first check the application envorinment %% if that is not set we check the environment variable RELX_COLOR. -- cgit v1.2.3