copy_options
handlingSection: 31.12.13.4 [fs.op.copy] Status: Open Submitter: Davis Herring Opened: 2018-01-29 Last modified: 2020-09-06
Priority: 2
View all other issues in [fs.op.copy].
View all issues with Open status.
Discussion:
(The resolution of #3 resolves part of C++17 NB comment US 36.)
The handling of several options forfilesystem::copy()
is wrong:
Single-level directory copying is silently suppressed by any flag other than
copy_options::recursive
(even copy_options::directories_only
). Single-level
directory copying operates via using some unspecified flag to trigger this misfeature.
copy_options::create_symlinks
and copy_options::skip_symlinks
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).
The copy_options
groups for existing target files and the form of copying are
consulted only for creating regular files.
copy("file", "dir")
creates dir/file, but copy("symlink", "dir",
copy_options::copy_symlinks)
fails.
If a symlink is encountered with copy_options::copy_symlinks
and
copy_options::create_symlinks
, the latter flag is ignored (but its otherwise sensible
restriction to absolute paths applies) rather than the former.
copy_options::create_symlinks
without copy_options::copy_symlinks
fails if it encounters a symlink. (This is particularly damaging for recursive operation.)
This issue, since it replaces so much text, also addresses two error-handling concerns in passing:
The significance of equivalent(from, to)
failing is unspecified. (Ignoring such
failures entirely would make dangerous those operations that replace the target with a link.)
Copying a directory involves several operations. When an error_code
is being used,
the process continues past errors and (because successful functions call ec.clear()
)
may suppress them.
This expands on the resolution for LWG 2681.
This also addresses the same issue as LWG 2682, but has a different result (based on the fact that the Example successfully copies directories to new, non-existent names).[2018-06; Rapperswil Wednesday evening, discussing LWG 2682]
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 LWG 2682 and LWG 3057 and set a priority 2 and look at 3057 in San Diego
[2018-11 San Diego Thursday night issue processing]
Need to gather implementation experience; revisit in Kona. Status to Open.
[2018-11-13; Billy O'Neal comments]
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.
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. 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 thansymlink_status
) to after I've detected that
there's actually a symlink (or junction) involved.
Proposed resolution:
This wording is relative to N4750.
Modify Table 115 — "Enum class copy_options
" as indicated:
Option group controlling copy
andcopy_file
function effects for existing target filesConstant Meaning […] […]
Modify 31.12.13.4 [fs.op.copy] as indicated:
Rationale:
POSIX.1-2008 allows the implementation to create hard links "to" symbolic links, and provideslinkat()
to choose between the symlink and its target. 31.12.13.4 [fs.op.copy]/4.9 is redundant given 31.12.5 [fs.err.report]/3.1.
void copy(const path& from, const path& to, copy_options options); void copy(const path& from, const path& to, copy_options options, error_code& ec) noexcept;-3- Requires: At most one element from each option group (31.12.8.3 [fs.enum.copy.opts]) is set in
-4- Effects:options
.Before the first use off
andt
:If each is needed below,
(4.1) — If […]
[…]
(4.10) — Otherwise, no effects.auto linkf = (options & (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) && is_directory(t) ? to/from.filename() : to. bool linkt = (options & (copy_options::create_symlinks | copy_options::create_hard_links)) != copy_options::none || is_symlink(f); auto t2 = linkt ? symlink_status(to2) : status(to2);Effects are then as follows:[Drafting note:
copy_options::create_symlinks
is intentionally omitted for linkf; it may simply have been a typo forcopy_options::copy_symlinks
(which was added by LWG 2681) since at least N3940.]-5- Throws: As specified in 31.12.5 [fs.err.report]. -6- Remarks: For the signature with argument
(?.?) — If
f.type()
ort.type()
is an implementation-defined file type [fs.enum.file_type], then the effects are implementation-defined.[Drafting note: the text between the previous drafting note and this one is the only unchanged text under /4.]
(?.?) — Otherwise, if
exists(f)
isfalse
, report an error as specified in 31.12.5 [fs.err.report].(?.?) — Otherwise, do nothing if
(?.?.?) —
(options & copy_options::directories_only) != copy_options::none
andis_directory(f)
isfalse
, or(?.?.?) —
(options & copy_options::skip_symlinks) != copy_options::none
andis_symlink(f)
istrue
, or(?.?.?) —
(options & copy_options::skip_existing) != copy_options::none
andexists(t2)
istrue
.(?.?) — Otherwise, report an error as specified in 31.12.5 [fs.err.report] if:
(?.?.?) —
is_other(f) || is_other(t2)
istrue
, or(?.?.?) —
exists(t2) && exists(from) == exists(to2) && equivalent(from, to)
istrue
.(?.?) — Otherwise, if
is_directory(f)
istrue
, then:
(?.?.?) —
create_directory(to, from)
.(?.?.?) — If
(options & copy_options::recursive) != copy_options::none
or if(options & copy_options::directories_only) == copy_options::none
, iterate over the files infrom
, as if byfor (const directory_entry& x : directory_iterator(from)) if ((options & copy_options::recursive) != copy_options::none || !is_directory(linkf ? symlink_status(x.path()) : status(x.path()))) copy(x.path(), to/x.path().filename(), options);(?.?) — Otherwise, do nothing if
(options & copy_options::update_existing) != copy_options::none, exists(to2)
istrue
, andfrom
is not more recent thanto2
, determined as if by use of thelast_write_time
function ( [fs.op.last_write_time]).(?.?) — Otherwise, report an error as specified in 31.12.5 [fs.err.report] if:
(?.?.?) —
is_directory(t2)
istrue
, or(?.?.?) —
(options & (copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none
andexists(t2)
istrue
.(?.?) — Otherwise, if
linkt
istrue
, then:
(?.?.?) —
remove(to2)
if an existingto2
would prevent the following link creation.(?.?.?) — If
(options & copy_options::create_symlinks) != copy_options::none
,create_symlink(from, to2)
. [Note: Iffrom
is a symbolic link, it is not followed. — end note](?.?.?) — Otherwise, if
(options & copy_options::create_hard_links) != copy_options::none
, then create a hard link tofrom
, iflinkf
istrue
, or else to the file thatfrom
resolves to. [Note: Not all file systems that support hard links and symbolic links support creating hard links to symbolic links. — end note](?.?.?) — Otherwise,
copy_symlink(from, to2)
.(?.?) — Otherwise,
copy_file(from, to2, options)
.ec
, any library functions called by the implementation shall have anerror_code
argument if applicable. If any such function fails,copy
returns immediately without (further) modifyingec
.