Elixir and Erlang/OTP versions
Erlang/OTP 27 [erts-15.2.7.4] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit:ns]
Elixir 1.20.0-rc.4 (9e4eb3d) (compiled with Erlang/OTP 27)
Operating system
Guix System (NixOS-like Linux distro)
Current behavior
Read-only directories can't be copied recursively because destination directories inherit permissions from the source directory without postpone.
How to reproduce:
% mkdir src-dir
% touch src-dir/test
% chmod -w -R src-dir
% ~/src/elixir/elixir/bin/iex
iex(1)> File.cp_r!("src-dir", "dst-dir")
** (File.CopyError) could not copy recursively from "src-dir" to "dst-dir". src-dir/test: permission denied
(elixir 1.20.0-rc.4) lib/file.ex:1252: File.cp_r!/3
iex:1: (file)
As I've mentioned before, I use Guix System (and I'm a maintainer of Erlang and Elixir packages), which is immutable, just like a little bit more popular NixOS, so everything there is in read-only "store", i.e.:
% ls -ld /gnu/store/vwif63v4d5hbna1isr1iaamxqlqjiwdb-erlang-27.3.4.6/lib/erlang/erts-15.2.7.4/bin
dr-xr-xr-x 2 root root 4096 Jan 1 1970 /gnu/store/vwif63v4d5hbna1isr1iaamxqlqjiwdb-erlang-27.3.4.6/lib/erlang/erts-15.2.7.4/bin
Even with fix below applied, some tests fail:
1) test copy_erts/1 copies to directory (Mix.ReleaseTest)
test/mix/release_test.exs:664
** (File.Error) could not write to file "/home/igor/src/elixir/elixir/lib/mix/tmp/mix_release/_build/dev/rel/demo/erts-15.2.7.4/bin/erl": permission denied
code: assert copy_erts(release(include_erts: true))
stacktrace:
(elixir 1.20.0-rc.4) lib/file.ex:1477: File.write!/3
(mix 1.20.0-rc.4) lib/mix/release.ex:804: Mix.Release.copy_erts/1
test/mix/release_test.exs:665: (test)
2) test copy_app/2 does not copy OTP app if include_erts is false (Mix.ReleaseTest)
test/mix/release_test.exs:755
** (File.Error) could not remove files and directories recursively from "/home/igor/src/elixir/elixir/lib/mix/tmp/mix_release": file already exists
stacktrace:
(elixir 1.20.0-rc.4) lib/file.ex:1751: File.rm_rf!/1
test/mix/release_test.exs:30: Mix.ReleaseTest.__ex_unit_setup_1/1
test/mix/release_test.exs:7: Mix.ReleaseTest.__ex_unit__/2
...
64) test validates compile_env (Mix.Tasks.ReleaseTest)
/home/igor/src/elixir/elixir/lib/mix/test/mix/tasks/release_test.exs:542
** (EXIT from #PID<0.15884.0>) an exception was raised:
** (File.Error) could not write to file "/home/igor/src/elixir/elixir/lib/mix/tmp/Mix.Tasks.ReleaseTest/test validates compile_env/_build/dev/rel/compile_env_config/erts-15.2.7.4/bin/erl": permission denied
(elixir 1.20.0-rc.4) lib/file.ex:1477: File.write!/3
(mix 1.20.0-rc.4) lib/mix/release.ex:804: Mix.Release.copy_erts/1
(mix 1.20.0-rc.4) lib/mix/tasks/release.ex:1391: Mix.Tasks.Release.copy/2
(elixir 1.20.0-rc.4) lib/task/supervised.ex:105: Task.Supervised.invoke_mfa/2
(elixir 1.20.0-rc.4) lib/task/supervised.ex:40: Task.Supervised.reply/4
...
Expected behavior
iex(1)> File.cp_r!("src-dir", "dst-dir")
["dst-dir/test", "dst-dir"]
I guess, the problem itself is in lib/elixir/lib/file.ex, function do_cp_r/5. The following changes fix the behaviour:
...
success when success in [:ok, {:error, :eexist}] ->
case get_dir_mode(src, dest) do
{:ok, dest_fileinfo} ->
result =
Enum.reduce_while(files, [dest | acc], fn x, acc ->
case do_cp_r(
Path.join(src, x),
Path.join(dest, x),
on_conflict,
dereference,
acc
) do
{:error, _, _} = error -> {:halt, error}
acc -> {:cont, acc}
end
end)
write_stat(dest, dest_fileinfo)
result
...
defp get_dir_mode(src, dest) do
with {:ok, dest_fileinfo} <- stat(dest),
{:ok, src_fileinfo} <- stat(src) do
{:ok, %{dest_fileinfo | mode: src_fileinfo.mode}}
end
end
Elixir and Erlang/OTP versions
Erlang/OTP 27 [erts-15.2.7.4] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit:ns]
Elixir 1.20.0-rc.4 (9e4eb3d) (compiled with Erlang/OTP 27)
Operating system
Guix System (NixOS-like Linux distro)
Current behavior
Read-only directories can't be copied recursively because destination directories inherit permissions from the source directory without postpone.
How to reproduce:
As I've mentioned before, I use
Guix System(and I'm a maintainer ofErlangandElixirpackages), which is immutable, just like a little bit more popularNixOS, so everything there is in read-only "store", i.e.:Even with fix below applied, some tests fail:
Expected behavior
I guess, the problem itself is in
lib/elixir/lib/file.ex, functiondo_cp_r/5. The following changes fix the behaviour: