diff --git a/lib/elixir/lib/file.ex b/lib/elixir/lib/file.ex index 0c3e79931fb..a85d105c832 100644 --- a/lib/elixir/lib/file.ex +++ b/lib/elixir/lib/file.ex @@ -310,24 +310,23 @@ defmodule File do end defp do_mkdir_p(path) do - if dir?(path) do + parent = Path.dirname(path) + + if parent == path do :ok else - parent = Path.dirname(path) - - if parent == path do - # Protect against infinite loop - {:error, :einval} - else - _ = do_mkdir_p(parent) - - case :file.make_dir(path) do - {:error, :eexist} = error -> - if dir?(path), do: :ok, else: error + case do_mkdir_p(parent) do + :ok -> + case :file.make_dir(path) do + {:error, :eexist} -> + if dir?(path), do: :ok, else: {:error, :enotdir} + + other -> + other + end - other -> - other - end + e -> + e end end end diff --git a/lib/elixir/test/elixir/file_test.exs b/lib/elixir/test/elixir/file_test.exs index 47301ce1a7d..c053a1be76c 100644 --- a/lib/elixir/test/elixir/file_test.exs +++ b/lib/elixir/test/elixir/file_test.exs @@ -1146,6 +1146,28 @@ defmodule FileTest do File.mkdir_p!(invalid) end end + + @tag :unix + test "mkdir_p with non-accessible parent directory" do + fixture = tmp_path("tmp_test_parent") + + try do + refute File.exists?(fixture) + assert File.mkdir_p!(fixture) == :ok + %File.Stat{mode: orig_mode} = File.stat!(fixture) + assert File.chmod!(fixture, 0o000) == :ok + + child = Path.join(fixture, "child") + refute File.exists?(child) + + assert File.mkdir_p(child) == {:error, :eacces} + refute File.exists?(child) + + assert File.chmod!(fixture, orig_mode) == :ok + after + File.rm_rf(fixture) + end + end end describe "rm" do