This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++20 status.

### 2682. `filesystem::copy()` won't create a symlink to a directory

Section: 31.12.13.4 [fs.op.copy] Status: C++20 Submitter: Jonathan Wakely Opened: 2016-04-19 Last modified: 2021-02-25

Priority: 2

View all other issues in [fs.op.copy].

View all issues with C++20 status.

Discussion:

(First raised in c++std-lib-38544)

`filesystem::copy` doesn't create a symlink to a directory in this case:

> `copy("/", "root", copy_options::create_symlinks);`

If the first path is a file then a symlink is created, but I think my implementation is correct to do nothing for a directory. We get to bullet 31.12.13.4 [fs.op.copy] (3.6) where `is_directory(f)` is true, but `options == create_symlinks`, so we go to the next bullet (3.7) which says "Otherwise, no effects."

I think the case above should either create a symlink, or should report an error. GNU cp -s gives an error in this case, printing "omitting directory '/'". An error seems reasonable, you can use `create_symlink` to create a symlink to a directory.

[2016-05 Issues Telecon]

This is related to 2681; and should be considered together.

[2016-08 Chicago]

Wed AM: Move to Tentatively Ready

Previous resolution [SUPERSEDED]:

> Add a new bullet following (3.6) in 31.12.13.4 [fs.op.copy] as shown:

> > - If `!exists(t)`, then `create_directory(to, from)`.

> > - Then, iterate over the files in `from`, as if by `for (directory_entry& x : directory_iterator(from))`, and for each iteration `copy(x.path(), to/x.path().filename(), options | copy_options::unspecified )`

> > - Otherwise, if `is_directory(f) && (options & copy_options::create_symlinks) != copy_options::none`, then report an error with an `error_code` argument equal to `make_error_code(errc::is_a_directory)`.

> > - Otherwise, no effects.

[2016-10-16, Eric reopens and provides improved wording]

The current PR makes using `copy(...)` to copy/create a directory symlink an error. For example, the following is now an error:

>     copy("/", "root", copy_options::create_symlinks);

However the current PR doesn't handle the case where both `copy_options::create_symlinks` and `copy_options::recursive` are specified. This case is still incorrectly handled by bullet (3.6) [fs.op.copy].

I suggest we move the PR before this bullet so that it catches the recursive copy case, since currently the conditions are ordered:

> 3.6 Otherwise if `is_directory(f) && (bool(options & copy_options::recursive) || ...)`
> 3.X Otherwise if `is_directory(f) && bool(options & copy_options::create_symlinks)`

So 3.6 catches `create_symlinks | recursive` but I believe we want 3.X to handle it instead.

[2018-01-26 issues processing telecon]

Status to 'Review'; we think this is OK but want some implementation experience before adopting it.

[2018-01-29 Jonathan says]

The proposed resolution for LWG 2682 has been in GCC's Filesystem TS implementation for more than a year. It's also in our `std::filesystem` implementation on the subversion trunk.

[2018-06; Rapperswil Wednesday evening]

JW: can we use the words we are shipping already since two years?
BO: what we got is better than what we had before
no objection to moving to Ready
ACTION move to Ready
ACTION link 2682 and LWG 3057 and set a priority 2 and look at 3057 in San Diego

Daniel rebases wording to N4750.

[2018-11, Adopted in San Diego]

Proposed resolution:

This wording is relative to N4750.

1. Add a new bullet before (4.8) in 31.12.13.4 [fs.op.copy] as shown:

   > 1. (4.7) — Otherwise, if `is_regular_file(f)`, then:

   >    > […]

   > 2. (4.?) — Otherwise, if

   >    > >     is_directory(f) &&
   >    > >     (options & copy_options::create_symlinks) != copy_options::none

   >    > then report an error with an `error_code` argument equal to `make_error_code(errc::is_a_directory)`.

   > 3. (4.8) — Otherwise, if

   >    > >     is_directory(f) &&
   >    > >     ((options & copy_options::recursive) != copy_options::none ||
   >    > >     options == copy_options::none)

   >    > then:

   >    1. (4.8.1) — If `exists(t)` is `false`, then `create_directory(to, from)`.

   >    2. (4.8.2) — Then, iterate over the files in `from`, as if by

   >       >     for (const directory_entry& x : directory_iterator(from))
   >       >       copy(x.path(), to/x.path().filename(),
   >       >         options | copy_options::in-recursive-copy);

   >       where `in-recursive-copy` is a bitmask element of `copy_options` that is not one of the elements in 31.12.8.3 [fs.enum.copy.opts].

   > 4. (4.9) — Otherwise, for the signature with argument `ec`, `ec.clear()`.

   > 5. (4.10) — Otherwise, no effects.