Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/elixir/lib/file.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,8 @@ defmodule File do
explicitly disallow this behavior. If `source` is a `file` and `destination`
is a directory, `{:error, :eisdir}` will be returned.

Special files such as device files, sockets, and named pipes are not copied.

## Options

* `:on_conflict` - (since v1.14.0) Invoked when a file already exists in the destination.
Expand Down Expand Up @@ -1262,7 +1264,7 @@ defmodule File do
end

{:ok, _} ->
{:error, :eio, src}
acc

{:error, reason} ->
{:error, reason, src}
Expand Down
29 changes: 29 additions & 0 deletions lib/elixir/test/elixir/file_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,35 @@ defmodule FileTest do
%File.Stat{mode: dest_mode} = File.stat!(dest)
assert src_mode == dest_mode
end

@tag :unix
test "cp_r skips sockets and other special files" do
src = tmp_path("src_with_socket")
dest = tmp_path("dest_with_socket")
socket_path = Path.join(src, "test.sock")
regular_file = Path.join(src, "regular.txt")

File.mkdir_p!(src)
File.write!(regular_file, "content")

{:ok, socket} = :gen_tcp.listen(0, [:local, {:ifaddr, {:local, socket_path}}])

try do
assert File.exists?(socket_path)
assert :elixir_utils.read_link_type(socket_path) == {:ok, :other}

{:ok, copied_files} = File.cp_r(src, dest)

assert Path.join(dest, "regular.txt") in copied_files

refute File.exists?(Path.join(dest, "test.sock"))
refute Path.join(dest, "test.sock") in copied_files
after
:gen_tcp.close(socket)
File.rm_rf(src)
File.rm_rf(dest)
end
end
end

defmodule Queries do
Expand Down