<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 3057: Correct copy_options handling</title>
<meta property="og:title" content="Issue 3057: Correct copy_options handling">
<meta property="og:description" content="C++ library issue. Status: Open">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue3057.html">
<meta property="og:type" content="website">
<meta property="og:image" content="http://cplusplus.github.io/LWG/images/cpp_logo.png">
<meta property="og:image:alt" content="C++ logo">
<style>
  p {text-align:justify}
  li {text-align:justify}
  pre code.backtick::before { content: "`" }
  pre code.backtick::after { content: "`" }
  blockquote.note
  {
    background-color:#E0E0E0;
    padding-left: 15px;
    padding-right: 15px;
    padding-top: 1px;
    padding-bottom: 1px;
  }
  ins {background-color:#A0FFA0}
  del {background-color:#FFA0A0}
  table.issues-index { border: 1px solid; border-collapse: collapse; }
  table.issues-index th { text-align: center; padding: 4px; border: 1px solid; }
  table.issues-index td { padding: 4px; border: 1px solid; }
  table.issues-index td:nth-child(1) { text-align: right; }
  table.issues-index td:nth-child(2) { text-align: left; }
  table.issues-index td:nth-child(3) { text-align: left; }
  table.issues-index td:nth-child(4) { text-align: left; }
  table.issues-index td:nth-child(5) { text-align: center; }
  table.issues-index td:nth-child(6) { text-align: center; }
  table.issues-index td:nth-child(7) { text-align: left; }
  table.issues-index td:nth-child(5) span.no-pr { color: red; }
  @media (prefers-color-scheme: dark) {
     html {
        color: #ddd;
        background-color: black;
     }
     ins {
        background-color: #225522
     }
     del {
        background-color: #662222
     }
     a {
        color: #6af
     }
     a:visited {
        color: #6af
     }
     blockquote.note
     {
        background-color: rgba(255, 255, 255, .10)
     }
  }
</style>
</head>
<body>
<hr>
<p><em>This page is a snapshot from the LWG issues list, see the <a href="lwg-active.html">Library Active Issues List</a> for more information and the meaning of <a href="lwg-active.html#Open">Open</a> status.</em></p>
<h3 id="3057"><a href="lwg-active.html#3057">3057</a>. Correct <code>copy_options</code> handling</h3>
<p><b>Section:</b> 31.12.13.4 <a href="https://wg21.link/fs.op.copy">[fs.op.copy]</a> <b>Status:</b> <a href="lwg-active.html#Open">Open</a>
 <b>Submitter:</b> Davis Herring <b>Opened:</b> 2018-01-29 <b>Last modified:</b> 2020-09-06</p>
<p><b>Priority: </b>2
</p>
<p><b>View all other</b> <a href="lwg-index.html#fs.op.copy">issues</a> in [fs.op.copy].</p>
<p><b>View all issues with</b> <a href="lwg-status.html#Open">Open</a> status.</p>
<p><b>Discussion:</b></p>
<p>
(The resolution of #3 resolves part of <a href="https://wg21.link/p0488r0#page=10">C++17 NB comment US 36</a>.) 
<p/>
The handling of several options for <code>filesystem::copy()</code> is wrong:
</p>
<ol>
<li><p>Single-level directory copying is silently suppressed by any flag other than 
<code>copy_options::recursive</code> (even <code>copy_options::directories_only</code>). Single-level 
directory copying operates via using some unspecified flag to trigger this misfeature.</p></li>
<li><p><code>copy_options::create_symlinks</code> and <code>copy_options::skip_symlinks</code> affect 
the interpretation of the destination name; the latter shouldn't ever, and the former should 
affect only broken symlinks (since it would want to replace them).</p></li>
<li><p>The <code>copy_options</code> groups for existing target files and the form of copying are 
consulted only for creating regular files.</p></li>
<li><p><code>copy("file", "dir")</code> creates dir/file, but <code>copy("symlink", "dir", 
copy_options::copy_symlinks)</code> fails.</p></li>
<li><p>If a symlink is encountered with <code>copy_options::copy_symlinks</code> and 
<code>copy_options::create_symlinks</code>, the latter flag is ignored (but its otherwise sensible 
restriction to absolute paths applies) rather than the former.</p></li>
<li><p><code>copy_options::create_symlinks</code> without <code>copy_options::copy_symlinks</code> 
fails if it encounters a symlink. (This is particularly damaging for recursive operation.)</p></li>
</ol>
<p>
This issue, since it replaces so much text, also addresses two error-handling concerns in passing:
</p>
<ol>
<li><p>The significance of <code>equivalent(from, to)</code> failing is unspecified. (Ignoring such 
failures entirely would make dangerous those operations that replace the target with a link.)</p></li>
<li><p>Copying a directory involves several operations. When an <code>error_code</code> is being used, 
the process continues past errors and (because successful functions call <code>ec.clear()</code>) 
may suppress them.</p></li>
</ol>
<p>
This expands on the resolution for LWG <a href="lwg-defects.html#2681" title="filesystem::copy() cannot copy symlinks (Status: C++17)">2681</a><sup><a href="https://cplusplus.github.io/LWG/issue2681" title="Latest snapshot">(i)</a></sup>.
<p/>
This also addresses the same issue as LWG <a href="lwg-defects.html#2682" title="filesystem::copy() won't create a symlink to a directory (Status: C++20)">2682</a><sup><a href="https://cplusplus.github.io/LWG/issue2682" title="Latest snapshot">(i)</a></sup>, but has a different result 
(based on the fact that the Example successfully copies directories to new, non-existent names).
</p>

<p><i>[2018-06; Rapperswil Wednesday evening, discussing LWG <a href="lwg-defects.html#2682" title="filesystem::copy() won't create a symlink to a directory (Status: C++20)">2682</a><sup><a href="https://cplusplus.github.io/LWG/issue2682" title="Latest snapshot">(i)</a></sup>]</i></p>

<p>
JW: can we use the words we are shipping already since two years?<br/>
BO: what we got is better than what we had before<br/>
no objection to moving to Ready<br/>
ACTION move to Ready<br/>
ACTION link LWG <a href="lwg-defects.html#2682" title="filesystem::copy() won't create a symlink to a directory (Status: C++20)">2682</a><sup><a href="https://cplusplus.github.io/LWG/issue2682" title="Latest snapshot">(i)</a></sup> and LWG 3057 and set a priority 2 and look at 3057 in San Diego 
</p>

<p><i>[2018-11 San Diego Thursday night issue processing]</i></p>

<p>Need to gather implementation experience; revisit in Kona. Status to Open.</p>

<p><i>[2018-11-13; Billy O'Neal comments]</i></p>

<p>
I (Billy O'Neal) prefer Davis' solution to LWG 3057, as I think the wording follows the meaning of the individual 
enum values more closely, and enables more scenarios to function correctly instead of reporting such cases as errors.
<p/>
However, I don't want to adopt that wording as is because it requires my implementation to detect errors in places 
that force us to do a bunch of extra system calls, and I don't believe those specific ways error handling happens 
is relevant to what the copy API wants to do.
<p/>
Ideally, the wording would be structured such that it said "here's a list of error conditions, if they happen we 
aren't going to tell you when exactly they are detected" and then listed the behavior irrespective of when errors 
happen. That way implementations can do the error checks when it makes sense according to what their system APIs 
report. For example, anything that requires symlink resolution is very expensive on my platform so I'd want to 
be able to defer anything related to status (rather than <code>symlink_status</code>) to after I've detected that 
there's actually a symlink (or junction) involved.
</p>


<p id="res-3057"><b>Proposed resolution:</b></p>
<p>This wording is relative to <a href="https://wg21.link/n4750">N4750</a>.</p>

<ol>
<li><p>Modify Table 115 &mdash; "Enum class <code>copy_options</code>" as indicated:</p>

<blockquote>
 <table border="1" cellpadding="5" cellspacing="0" style="border-collapse: collapse">
  <tr>
    <th colspan="2">Option group controlling <ins><code>copy</code> and</ins> <code>copy_file</code> 
    function effects for existing target files</th>
  </tr>
    <tr>
      <td><b>Constant</b></td>
      <td><b>Meaning</b></td>
    </tr>
    <tr>
      <td>[&hellip;]</td>
      <td>[&hellip;]</td>
    </tr>
  </table>
</blockquote>

</li>

<li><p>Modify 31.12.13.4 <a href="https://wg21.link/fs.op.copy">[fs.op.copy]</a> as indicated:</p>

<blockquote class="note">
<p>
<b>Rationale:</b>
<p/>
POSIX.1-2008 allows the implementation to create hard links "to" symbolic links, and provides 
<code>linkat()</code> to choose between the symlink and its target.
<p/>
31.12.13.4 <a href="https://wg21.link/fs.op.copy">[fs.op.copy]</a>/4.9 is redundant given 31.12.5 <a href="https://wg21.link/fs.err.report">[fs.err.report]</a>/3.1.
</p>
</blockquote>

<blockquote>
<pre>
void copy(const path&amp; from, const path&amp; to, copy_options options);
void copy(const path&amp; from, const path&amp; to, copy_options options,
          error_code&amp; ec) noexcept;
</pre>
<blockquote>
<p>
-3- <i>Requires:</i> At most one element from each option group (31.12.8.3 <a href="https://wg21.link/fs.enum.copy.opts">[fs.enum.copy.opts]</a>) 
is set in <code>options</code>.
<p/>
-4- <i>Effects:</i> <del>Before the first use of <code>f</code> and <code>t</code>:</del>
<ol style="list-style-type: none">
<li><p><del>(4.1) &mdash; If [&hellip;]</del></p></li>
<li><p><del>[&hellip;]</del></p></li>
<li><p><del>(4.10) &mdash; Otherwise, no effects.</del></p></li>
</ol>
<ins>If each is needed below,</ins>
<blockquote><pre>
<ins>auto linkf = (options &amp; (copy_options::copy_symlinks |
                         copy_options::skip_symlinks)) != copy_options::none;
auto f = linkf ? symlink_status(from) : status(from), t = status(to);
auto to2 = !is_directory(f) &amp;&amp; is_directory(t) ? to/from.filename() : to.
bool linkt = (options &amp; (copy_options::create_symlinks |
                         copy_options::create_hard_links)) != copy_options::none ||
            is_symlink(f);
auto t2 = linkt ? symlink_status(to2) : status(to2);</ins>
</pre></blockquote>

<blockquote class="note">
<p>
[<i>Drafting note:</i> <code>copy_options::create_symlinks</code> is intentionally omitted for linkf; 
it may simply have been a typo for <code>copy_options::copy_symlinks</code> (which was added by LWG 
<a href="lwg-defects.html#2681" title="filesystem::copy() cannot copy symlinks (Status: C++17)">2681</a><sup><a href="https://cplusplus.github.io/LWG/issue2681" title="Latest snapshot">(i)</a></sup>) since at least <a href="https://wg21.link/n3940">N3940</a>.]
</p>
</blockquote>

<ins>Effects are then as follows:</ins>
<ol style="list-style-type: none">
<li><p><ins>(?.?) &mdash; If <code>f.type()</code> or <code>t.type()</code> is an implementation-defined 
file type  [fs.enum.file_type], then the effects are implementation-defined.</ins></p>
<blockquote class="note">
<p>
[<i>Drafting note:</i> the text between the previous drafting note and this one is the only unchanged 
text under /4.]
</p>
</blockquote>
</li>
<li><p><ins>(?.?) &mdash; Otherwise, if <code>exists(f)</code> is <code>false</code>, report an error as 
specified in 31.12.5 <a href="https://wg21.link/fs.err.report">[fs.err.report]</a>.</ins></p></li>
<li><p><ins>(?.?) &mdash; Otherwise, do nothing if</ins></p>
<ol style="list-style-type: none">
<li><p><ins>(?.?.?) &mdash; <code>(options &amp; copy_options::directories_only) != copy_options::none</code> 
and <code>is_directory(f)</code> is <code>false</code>, or</ins></p></li>
<li><p><ins>(?.?.?) &mdash; <code>(options &amp; copy_options::skip_symlinks) != copy_options::none</code> 
and <code>is_symlink(f)</code> is <code>true</code>, or</ins></p></li>
<li><p><ins>(?.?.?) &mdash; <code>(options &amp; copy_options::skip_existing) != copy_options::none</code> 
and <code>exists(t2)</code> is <code>true</code>.</ins></p></li>
</ol>
</li>
<li><p><ins>(?.?) &mdash; Otherwise, report an error as specified in 31.12.5 <a href="https://wg21.link/fs.err.report">[fs.err.report]</a> 
if:</ins></p>
<ol style="list-style-type: none">
<li><p><ins>(?.?.?) &mdash; <code>is_other(f) || is_other(t2)</code> is <code>true</code>, or</ins></p></li>
<li><p><ins>(?.?.?) &mdash; <code>exists(t2) &amp;&amp; exists(from) == exists(to2) &amp;&amp; 
equivalent(from, to)</code> is <code>true</code>.</ins></p></li>
</ol>
</li>
<li><p><ins>(?.?) &mdash; Otherwise, if <code>is_directory(f)</code> is <code>true</code>, then:</ins></p>
<ol style="list-style-type: none">
<li><p><ins>(?.?.?) &mdash; <code>create_directory(to, from)</code>.</ins></p></li>
<li><p><ins>(?.?.?) &mdash; If <code>(options &amp; copy_options::recursive) != copy_options::none</code> 
or if <code>(options &amp; copy_options::directories_only) == copy_options::none</code>, iterate over 
the files in <code>from</code>, as if by</ins></p>
<blockquote><pre>
<ins>for (const directory_entry&amp; x : directory_iterator(from))
  if ((options &amp; copy_options::recursive) != copy_options::none ||
     !is_directory(linkf ? symlink_status(x.path()) : status(x.path())))
       copy(x.path(), to/x.path().filename(), options);</ins>
</pre></blockquote>
</li>
</ol>
</li>
<li><p><ins>(?.?) &mdash; Otherwise, do nothing if <code>(options &amp; copy_options::update_existing) 
!= copy_options::none, exists(to2)</code> is <code>true</code>, and <code>from</code> is not more recent than 
<code>to2</code>, determined as if by use of the <code>last_write_time</code> function 
( [fs.op.last_write_time]).</ins></p></li>
<li><p><ins>(?.?) &mdash; Otherwise, report an error as specified in 31.12.5 <a href="https://wg21.link/fs.err.report">[fs.err.report]</a> 
if:</ins></p>
<ol style="list-style-type: none">
<li><p><ins>(?.?.?) &mdash; <code>is_directory(t2)</code> is <code>true</code>, or</ins></p></li>
<li><p><ins>(?.?.?) &mdash; <code>(options &amp; (copy_options::overwrite_existing |
copy_options::update_existing)) == copy_options::none</code> and <code>exists(t2)</code> is <code>true</code>.</ins></p></li>
</ol>
</li>
<li><p><ins>(?.?) &mdash; Otherwise, if <code>linkt</code> is <code>true</code>, then:</ins></p>
<ol style="list-style-type: none">
<li><p><ins>(?.?.?) &mdash; <code>remove(to2)</code> if an existing <code>to2</code> would prevent the 
following link creation.</ins></p></li>
<li><p><ins>(?.?.?) &mdash; If <code>(options &amp; copy_options::create_symlinks) 
!= copy_options::none</code>, <code>create_symlink(from, to2)</code>. [<i>Note:</i> If <code>from</code> 
is a symbolic link, it is not followed. &mdash; <i>end note</i>]</ins></p></li>
<li><p><ins>(?.?.?) &mdash; Otherwise, if <code>(options &amp; copy_options::create_hard_links) 
!= copy_options::none</code>, then create a hard link to <code>from</code>, if <code>linkf</code> is <code>true</code>, 
or else to the file that <code>from</code> resolves to. [<i>Note:</i> Not all file systems that support 
hard links and symbolic links support creating hard links to symbolic links. &mdash; <i>end note</i>]</ins></p></li>
<li><p><ins>(?.?.?) &mdash; Otherwise, <code>copy_symlink(from, to2)</code>.</ins></p></li>
</ol>
</li>
<li><p><ins>(?.?) &mdash; Otherwise, <code>copy_file(from, to2, options)</code>.</ins></p></li>
</ol>
<p/>
-5- <i>Throws:</i> As specified in 31.12.5 <a href="https://wg21.link/fs.err.report">[fs.err.report]</a>.
<p/>
-6- <i>Remarks:</i> For the signature with argument <code>ec</code>, any library functions called by the 
implementation shall have an <code>error_code</code> argument if applicable. <ins>If any such function fails, 
<code>copy</code> returns immediately without (further) modifying <code>ec</code>.</ins>
</p>
</blockquote>
</blockquote>
</li>
</ol>





</body>
</html>
