   end
 
   @doc """
-  Copies the contents in `source` to `destination`.
-  Similar to the command `cp -r` in Unix systems,
-  this function behaves differently depending
-  if `source` and `destination` are a file or a directory.
-
-  If both are files, it simply copies `source` to
-  `destination`. However, if `destination` is a directory,
-  it copies the contents of `source` to `destination/source`
-  recursively.
-
-  If a file already exists in the destination,
-  it invokes a callback which should return
-  true if the existing file should be overriden,
-  false otherwise. It defaults to return true.
+  Copies the contents in `source` to `destination` preserving its mode.
+
+  Similar to the command `cp` in Unix systems, this function
+  behaves differently depending if `destination` is a directory
+  or not. In paritcular, if `destination` is a directory, it
+  copies the contents of `source` to `destination/source`.
+
+  If a file already exists in the destination, it invokes a
+  callback which should return true if the existing file
+  should be overriden, false otherwise. It defaults to return true.
 
   It returns `:ok` in case of success, returns
   `{ :error, reason }` otherwise.
+
+  If you want to copy contents from an io device to another device
+  or do a straight copy from a source to a destination without
+  preserving modes, check `File.copy/3` instead.
   """
   def cp(source, destination, callback // fn(_, _) -> true end) do
-    if dir?(source) do
-      { :error, :eisdir }
-    else
-      output =
-        if dir?(destination) do
-          mkdir(destination)
-          FN.join(destination, FN.basename(source))
-        else
-          destination
-        end
-
-      case do_cp_file(source, output, callback, []) do
-        { :error, reason, _ } -> { :error, reason }
-        _ -> :ok
+    output =
+      if dir?(destination) do
+        mkdir(destination)
+        FN.join(destination, FN.basename(source))
+      else
+        destination
       end
+
+    case do_cp_file(source, output, callback, []) do
+      { :error, reason, _ } -> { :error, reason }
+      _ -> :ok
     end
   end
 

      File.read!("invalid.txt")
      #=> raises File.Error

  In general, a developer should use the former in case
  he wants to react in the fie does not exist. The latter
  should be used when the developer expects his software
  to fail in case the file cannot be read (i.e. it is
  literally an exception).

  Finally, the functions in this module accept either
  a char lists or a binary. When manipulating paths, a char
  list is returned if one is given as argument. However,
  when reading files, binaries are always returned.
  """

  alias :file,     as: F

defmodule File do
  @moduledoc """
  This module contains function to manipulate files.

  Some of those functions are low-level, allowing the user
  to interact with the file or IO devices, like `File.open/2`,
  `File.copy/3` and others. This module also provides higher
  level functions, with their naming based on its UNIX variants.
  For example, one can copy a file via `File.cp/3` and remove
  files and directories recursively via `File.rm_rf/2`

  In order to write and read files, one must use the functions
  in the IO module. By default, a file is opened in binary mode
  which requires the functions `IO.binread`, `IO.binwrite` and
  `IO.binreadline` to interact with the file. A developer may
  pass `:utf8` as an option when opening the file and then all
  other functions from IO are available, since they work directly
  with Unicode data.

  Most of the functions in this module return `:ok` or
  `{ :ok, result }` in case of success, `{ :error, reason }`
  otherwise. Those function are also followed by a variant
  that ends with `!` which returns the result (without the
  `{ :ok, result }` tuple) in case of success or raises an
  exception in case it fails. For example:

      File.read("hello.txt")
      #=> { :ok, "World" }
