Document number: p0544R0
Date: 2017-02-01
Audience: Library Evolution Working Group
Reply to:
Titus Winters <titus@google.com>,
Geoff Romer <gromer@google.com>
The existing proposals in the Filesystem TS (now adopted for inclusion with
C++17) effectively specify the C++ filesystem API as a set of C++ wrappers and
types (like directory_iterator
) directly on top of whatever is
provided by the OS for filesystem interaction. This is easy to specify and
implement, but inflexible and likely does not provide the functionality needed
for a modern C++ codebase — for example, it is impossible to fake
filesystem errors in testing, or to use the same interfaces for in-memory
filesystems (avoiding the essential OS and I/O overhead). Our belief, based on
extensive experience in the Google codebase and production systems, is that
most robust C++ installations will wind up wrapping the proposed interface in
order to provide opportunities to change the backing store, simulate errors
for testing, etc. To that end, we propose the definition of a filesystem type,
and injection mechanisms to allow user-customization of the filesystem backend
used by the free functions provided by the Filesystem TS.
This proposal is pure-library, OS and compiler agnostic, and is backwards compatible with any existing code written against the Filesystem TS. It has not yet been implemented in this form. However, the filesystems at use in Google production work in roughly this way with small differences. In the Google design, multiple filesystems are present at once, they are all pinned into the filesystem at some root (/memfile, /virtualfs, etc) with the OS local filesystem as the generic fallback. This works for us because we have no symlinks. Once symlinks are in the equation, the design has to shift to something like what we are describing.
The biggest point of discussion in the design for filesystem injection is how to specify where injection takes place. Is it a new filesystem root? If so, how is it specified (especially on POSIX systems)? If you can inject at any point in a filesystem, does that injection respect symlinks? If so, that requires one (or more) OS calls on every filesystem call to resolve symlinks in order to determine if the specified path(s) fall inside the injection point. Since we cannot determine one policy that will satisfy all users for the above questions, we instead choose to present a more general injection mechanism: replace the filesystem entirely. In conjunction with the ability to get the “default” filesystem, a user can then build a solution that forwards to the underlying filesystem in a fashion congruent with any of the above designs.
Under this design, the expected cost is bounded by following an atomic pointer plus a vtable dereference. In comparison to the overhead of performing I/O through the OS, this is expected to be negligible.
Changes are relative to N4582.
Add the following to the <filesystem>
synopsis in
[fs.filesystem.syn]:
…class filesystem_error; class directory_entry; class filesystem; class default_filesystem_impl; // exposition only class directory_traverser; class directory_iterator; // range access for directory_iterator directory_iterator begin(directory_iterator iter) noexcept; directory_iterator end(const directory_iterator&) noexcept; bool operator==(const directory_iterator& lhs, const directory_iterator& rhs); bool operator!=(const directory_iterator& lhs, const directory_iterator& rhs); class recursive_directory_iterator;…typedef chrono::time_point<trivial-clock> file_time_type; // filesystem injection extern atomic<filesystem*> current_filesystem; // exposition only void inject_filesystem(filesystem& fs); filesystem& default_filesystem(); // filesystem operation functions…
Add a new paragraph to the end of [fs.err.report] as follows:
When a function that does not take an
error_code
parameter is specified to call a function that takes anerror_code
parameter, the outer function is understood to construct a localerror_code
variableec
to pass to the function call. If!ec
after the call completes, unless otherwise specified the function throws afilesystem_error
exception containingec
, an implementation-defined message, and any paths that were passed to the function.
Insert new sections above [class.directory_iterator]:
27.10.? Class
filesystem
[class.filesystem]Class
filesystem
defines the base class for the types of objects that provide access to the fundamental operations of a specific file system. A specialfilesystem
object ([class.default_filesystem_impl]) provides access to the native file system provided by the operating system, and otherfilesystem
objects can implement virtual file systems for purposes such as testing.Classes derived from
filesystem
shall satisfy all of the requirements offilesystem
.class filesystem { public: filesystem() = default; virtual ~filesystem(); filesystem(const filesystem&) = delete; filesystem& operator=(const filesystem&) = delete; virtual bool copy_file(const path& from, const path& to, copy_options options, error_code& ec) noexcept = 0; virtual bool create_directory(const path& p, error_code& ec) noexcept = 0; virtual bool create_directory(const path& p, const path& existing_p, error_code& ec) noexcept = 0; virtual void create_directory_symlink( const path& to, const path& new_symlink, error_code& ec) noexcept = 0; virtual void create_hard_link(const path& to, const path& new_hard_link, error_code& ec) noexcept = 0; virtual void create_symlink(const path& to, const path& new_symlink, error_code& ec) noexcept = 0; virtual path current_path(error_code& ec) const = 0; virtual void current_path(const path& p, error_code& ec) noexcept = 0; virtual bool equivalent(const path& p1, const path& p2, error_code& ec) const noexcept = 0; virtual uintmax_t file_size(const path& p, error_code& ec) const noexcept = 0; virtual uintmax_t hard_link_count(const path& p, error_code& ec) const noexcept = 0; virtual file_time_type last_write_time(const path& p, error_code& ec) const noexcept = 0; virtual void last_write_time(const path& p, file_time_type new_time, error_code& ec) const noexcept = 0; virtual void permissions(const path& p, perms prms, error_code& ec) const noexcept = 0; virtual path read_symlink(const path& p, error_code& ec) const = 0; virtual bool remove(const path& p, error_code& ec) noexcept = 0; virtual void rename(const path& old_p, const path& new_p, error_code& ec) noexcept = 0; virtual void resize_file(const path& p, uintmax_t new_size, error_code& ec) noexcept = 0; virtual space_info space(const path& p, error_code& ec) const noexcept = 0; virtual file_status status(const path& p, error_code& ec) const noexcept = 0; virtual file_status symlink_status(const path& p, error_code& ec) const noexcept = 0; virtual path system_complete(const path& p, error_code& ec) const = 0; virtual path temp_directory_path(error_code& ec) = 0; virtual unique_ptr<directory_traverser> directory_traverser( const path& p, directory_options options, error_code& ec) const = 0; };27.10.?.1
filesystem
members [filesystem.members]27.10.?.1.1 Copy file [filesystem.copy_file]
virtual bool copy_file(const path& from, const path& to, copy_options options, error_code& ec) noexcept = 0;
Copy [fs.op.copy_file] paragraphs 3 to 9 here, with edits as specified:
- Requires: At most one constant from each
copy_options
group is present inoptions
.- Effects: Report a file already exists error as specified in Error reporting (27.5.6.5) if:
Otherwise copy the contents and attributes of the file
exists(to)
andequivalent(from, to)
, orexists(to)
and(options & (copy_options::skip_existing | copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none
.from
resolves to to the fileto
resolves to if:Otherwise no effects.
!exists(to)
, orexists(to)
and(options & copy_options::overwrite_existing) != copy_options::none
, orexists(to)
and(options & copy_options::update_existing) != copy_options::none
, andfrom
is more recent thanto
, determined as if by use of thelast_write_time
function.- Returns:
true
if thefrom
file was copied, otherwisefalse
.The signature with argumentReturnec
rfalse
if an error occurs.Throws: As specified in Error reporting (27.5.6.5).- Complexity: At most one direct or indirect invocation of
status(to)
.
27.10.?.1.2 Create directory [filesystem.create_directory]
virtual bool create_directory(const path& p, error_code& ec) noexcept = 0;
Copy [fs.op.create_directory] paragraphs 1 to 4 here, with edits as specified:
- Effects: Establishes the postcondition by attempting to create the directory
p
resolves to, as if by POSIX. Creation failure becausemkdir()
with a second argument ofstatic_cast<int>(perms::all)
p
resolves to an existing directory shall not be treated as an error.- Postcondition:
is_directory(p)
.- Returns:
true
if a new directory was created, otherwisefalse
.The signature with argumentReturnsec
rfalse
if an error occurs.Throws: As specified in Error reporting (27.5.6.5).
virtual bool create_directory(const path& p, const path& existing_p, error_code& ec) noexcept = 0;
Copy [fs.op.create_directory] paragraphs 5 to 8 here, with edits as specified:
- Effects: Establishes the postcondition by attempting to create the directory
p
resolves to, with attributes copied from directoryexisting_p
. The set of attributes copied is operating system dependent. Creation failure becausep
resolves to an existing directory shall not be treated as an error. [Note: For POSIX based operating systems the attributes are those copied by native APIstat(existing_p.c_str(), &attributes_stat)
followed bymkdir(p.c_str(), attributes_stat.st_mode)
. For Windows based operating systems the attributes are those copied by native APICreateDirectoryExW(existing_p.c_str(), p.c_str(), 0)
. — end note]- Postcondition:
is_directory(p)
.- Returns:
true
if a new directory was created, otherwisefalse
.The signature with argumentReturns false if an error occurs.ec
rThrows: As specified in Error reporting (27.5.6.5).
27.10.?.1.3 Create directory symlink [filesystem.create_directory_symlink]
virtual void create_directory_symlink( const path& to, const path& new_symlink, error_code& ec) noexcept = 0;
Copy [fs.op.create_dir_symlk] paragraphs 1 to 5 here, with edits as specified:
- Effects: Establishes the postcondition
, as if by POSIX.symlink()
- Postcondition:
new_symlink
resolves to a symbolic link file that contains an unspecified representation ofto
.Throws: As specified in Error reporting (27.5.6.5).[Note: Some operating systems require symlink creation to identify that the link is to a directory. Portable code should usecreate_directory_symlink()
to create directory symlinks rather thancreate_symlink()
— end note][Note: Some operating systems do not support symbolic links at all or support them only for regular files. Some file systems (such as the FAT file system) do not support symbolic links regardless of the operating system. — end note]
27.10.?.1.4 Create hard link [filesystem.create_hard_link]
virtual void create_hard_link(const path& to, const path& new_hard_link, error_code& ec) noexcept = 0;
Copy [fs.op.create_hard_lk] paragraphs 1 to 4 here, with edits as specified:
- Effects: Establishes the postcondition
, as if by POSIX.link()
- Postcondition:
exists(to) && exists(new_hard_link) && equivalent(to, new_hard_link)
- The contents of the file or directory
to
resolves to are unchanged.Throws: As specified in Error reporting (27.5.6.5).[Note: Some operating systems do not support hard links at all or support them only for regular files. Some file systems (such as the FAT file system) do not support hard links regardless of the operating system. Some file systems limit the number of links per file. — end note]
27.10.?.1.5 Create symlink [filesystem.create_symlink]
virtual void create_symlink(const path& to, const path& new_symlink, error_code& ec) noexcept = 0;
Copy [fs.op.create_symlink] paragraphs 1 to 4 here, with edits as specified:
- Effects: Establishes the postcondition
, as if by POSIX.symlink()
- Postcondition:
new_symlink
resolves to a symbolic link file that contains an unspecified representation ofto
.Throws: As specified in Error reporting (27.5.6.5).[Note: Some operating systems do not support symbolic links at all or support them only for regular files. Some file systems (such as the FAT file system) do not support symbolic links regardless of the operating system. — end note]
27.10.?.1.6 Current path [filesystem.current_path]
virtual path current_path(error_code& ec) const = 0;
Copy [fs.op.current_path] paragraphs 1 to 5 here, with edits as specified:
- Returns: The absolute path of the current working directory
, obtained as if by POSIX.getcwd()
The signature with argumentReturnsec
rpath()
if an error occurs.Throws: As specified in Error reporting (27.5.6.5).- Remarks: The current working directory is the directory, associated with the process, that is used as the starting location in pathname resolution for relative paths.
[Note: The current_path() name was chosen to emphasize that the return is a path, not just a single directory name.The current path as returned by many operating systems is a dangerous global variable. It may be changed unexpectedly by a third-party or system library functions, or by another thread. — end note]
virtual void current_path(const path& p, error_code& ec) noexcept = 0;
Copy [fs.op.current_path] paragraphs 6 to 9 here, with edits as specified:
- Effects: Establishes the postcondition
, as if by POSIX.chdir()
- Postcondition:
equivalent(p, current_path())
.Throws: As specified in Error reporting (27.5.6.5).[Note: The current path for many operating systems is a dangerous global state. It may be changed unexpectedly by a third-party or system library functions, or by another thread. — end note]
27.10.?.1.7 Equivalent [filesystem.equivalent]
virtual bool equivalent(const path& p1, const path& p2, error_code& ec) const noexcept = 0
Copy [fs.op.equivalent] paragraphs 1 to 4 here, with edits as specified:
- Effects: Determines
file_status s1
ands2
, as if bystatus(p1)
andstatus(p2)
, respectively.- Returns:
true
, ifs1 == s2
andp1
andp2
resolve to the same file system entity, elsefalse
.The signature with argumentReturnsec
rfalse
if an error occurs.- Two paths are considered to resolve to the same file system entity if two candidate entities reside on the same device at the same location.
This is determined as if by the values of the POSIXstat
structure, obtained as if bystat()
for the two paths, having equalst_dev
values and equalst_ino
values.Throws:filesystem_error
if(!exists(s2)) || (is_other(s1) && is_other(s2))
, otherwise as specified in Error reporting (27.5.6.5).
27.10.?.1.8 File size [filesystem.file_size]
virtual uintmax_t file_size(const path& p, error_code& ec) const noexcept = 0;
Copy [fs.op.file_size] paragraphs 1 to 2 here, with edits as specified:
- Returns: If
!exists(p) || !is_regular_file(p)
an error is reported 27.5.6.5. Otherwise, the size in bytes of the filep
resolves to, determined as if by the value of the POSIX.stat
structure memberst_size
obtained as if by POSIXstat()
The signature with argumentReturnsec
rstatic_cast<uintmax_t>(-1)
if an error occurs.Throws: As specified in Error reporting (27.5.6.5).
27.10.?.1.9 Hard link count [filesystem.hard_link_count]
virtual uintmax_t hard_link_count(const path& p, error_code& ec) const noexcept = 0;
Copy [fs.op.hard_lk_ct] paragraphs 1 to 2 here, with edits as specified:
- Returns: The number of hard links for
p
.The signature with argumentReturnsec
rstatic_cast<uintmax_t>(-1)
if an error occurs.Throws: As specified in Error reporting (27.5.6.5).
27.10.?.1.10 Last write time [filesystem.last_write_time]
virtual file_time_type last_write_time(const path& p, error_code& ec) const noexcept = 0;
Copy [fs.op.last_write_time] paragraphs 1 to 2 here, with edits as specified:
- Returns: The time of last data modification of
p
, determined as if by the value of the POSIX.stat
structure memberst_mtime
obtained as if by POSIXstat()
The signature with argumentReturnsec
rfile_time_type::min()
if an error occurs.Throws: As specified in Error reporting (27.5.6.5).
virtual void last_write_time(const path& p, file_time_type new_time, error_code& ec) const noexcept = 0;
Copy [fs.op.last_write_time] paragraphs 3 to 5 here, with edits as specified:
- Effects: Sets the time of last modification of the file resolved to by
p
tonew_time
, as if by POSIX.futimens()
Throws: As specified in Error reporting (27.5.6.5).[Note: A postcondition oflast_write_time(p) == new_time
is not specified since it might not hold for file systems with coarse time granularity. — end note]
27.10.?.1.11 Permissions [filesystem.permissions]
virtual void permissions(const path& p, perms prms, error_code& ec) const noexcept = 0;
Copy [fs.op.permissions] paragraphs 1 to 4 here, with edits as specified:
- Requires:
!((prms & prms::add_perms) != perms::none<br> && (prms & perms::remove_perms) != perms::none)
.- Effects: Applies the effective permissions bits from
prms
to the filep
resolves to, as if by POSIX. The effective permission bits are determined as specified in Table 150.fchmodat()
Table 150 — Effects of permission bits Bits present in prms
Effective bits applied Neither add_perms
norremove_perms
prms && perms::mask
add_perms
status(p).permissions() | (prms & perms::mask)
remove_perms
status(p).permissions() & (prms & perms::mask)
- [Note: Conceptually permissions are viewed as bits, but the actual implementation may use some other mechanism. — end note]
Throws: As specified in Error reporting (27.5.6.5).
27.10.?.1.12 Read symlink [filesystem.read_symlink]
virtual path read_symlink(const path& p, error_code& ec) const noexcept = 0;
Copy [fs.op.read_symlink] paragraphs 1 to 2 here, with edits as specified:
- Returns: If
p
resolves to a symbolic link, apath
object containing the contents of that symbolic link.The signature with argumentec
returnspath()
if an error occurs.Throws: As specified in Error reporting (27.5.6.5).[Note: It is an error ifp
does not resolve to a symbolic link. — end note]
27.10.?.1.13 Remove [filesystem.remove]
virtual bool remove(const path& p, error_code& ec) noexcept = 0;
Copy [fs.op.remove] paragraphs 1 to 5 here, with edits as specified:
- Effects: If
exists(symlink_status(p,ec))
, it is removedas if by POSIX.remove()
- [Note: A symbolic link is itself removed, rather that the file it resolves to being removed. — end note]
- Postcondition:
!exists(symlink_status(p))
.- Returns:
false
ifp
did not exist, otherwisetrue
.The signature with argumentReturnsec
rfalse
if an error occurs.Throws: As specified in Error reporting (27.5.6.5).
27.10.?.1.14 Rename [filesystem.rename]
virtual void rename(const path& old_p, const path& new_p, error_code& ec) noexcept = 0;
Copy [fs.op.rename] paragraphs 1 to 3 here, with edits as specified:
- Effects: Renames
old_p
tonew_p
, as if by POSIX.rename()
- [Note: If
old_p
andnew_p
resolve to the same existing file, no action is taken. Otherwise, ifnew_p
resolves to an existing non-directory file, it is removed, while ifnew_p
resolves to an existing directory, it is removed if empty on POSIX compliant operating systems but is an error on some other operating systems. A symbolic link is itself renamed, rather than the file it resolves to being renamed. — end note]Throws: As specified in Error reporting (27.5.6.5).
27.10.?.1.15 Resize file [filesystem.resize_file]
virtual void resize_file(const path& p, uintmax_t new_size, error_code& ec) noexcept = 0;
Copy [fs.op.resize_file] paragraphs 1 to 3 here, with edits as specified:
- Postcondition:
file_size() == new_size
.Throws: As specified in Error reporting (27.5.6.5).Remarks: Achieves its postconditions as if by POSIXtruncate()
.
27.10.?.1.16 Space [filesystem.space]
virtual space_info space(const path& p, error_code& ec) const noexcept = 0;
Copy [fs.op.space] paragraphs 1 to 3 here, with edits as specified:
- Returns: An object of type
space_info
.The value of theThe object's members will be set as follows:space_info
object is determined as if by using POSIXstatvfs
to obtain a POSIXstruct statvfs
, and then multiplying itsf_blocks
,f_bfree
, andf_bavail
members by itsf_frsize
member, and assigning the results to thecapacity
,free
, andavailable
members respectively.Any members for which the value cannot be determined shall be set to
capacity
will be set to the total storage capacity of the storage volume containingp
.free
will be set to the total amount of free space on the storage volume containingp
.available
will be set to the amount of free space that is available to hold user data on the storage volume containingp
[Note: This can be less thanfree
because it excludes storage reserved for file system internals. — end note].static_cast<uintmax_t>(-1)
.For the signature with argumentAll members are set toec
, astatic_cast<uintmax_t>(-1)
if an error occurs.Throws: As specified in Error reporting (27.5.6.5).Remarks: The value of memberspace_info::available
is operating system dependent. [Note:available
may be less thanfree
. — end note]
27.10.?.1.17 Status [filesystem.status]
virtual file_status status(const path& p, error_code& ec) const noexcept = 0;
Copy [fs.op.status] paragraphs 4 to 9 here, with edits as specified:
- Effects: If possible, determines the attributes of the file
p
resolves to, as if by POSIX. If, during attribute determination, the underlying file system API reports an error, setsstat()
ec
to indicate the specific error reported. Otherwise,ec.clear()
.- [Note: This allows users to inspect the specifics of underlying API errors even when the value returned by
status()
is notfile_status(file_type::none)
. — end note]- Returns: If
ec != error_code()
:
- If the specific error indicates that
p
cannot be resolved because some element of the path does not exist, returnfile_status(file_type::not_found)
.- Otherwise, if the specific error indicates that
p
can be resolved but the attributes cannot be determined, returnfile_status(file_type::unknown)
.- Otherwise, return
file_status(file_type::none)
.[Note: These semantics distinguish between
p
being known not to exist,p
existing but not being able to determine its attributes, and there being an error that prevents even knowing ifp
exists. These distinctions are important to some use cases. — end note]Otherwise,
- If the attributes indicate a regular file,
as if by POSIXreturnS_ISREG
,file_status(file_type::regular)
. [Note:file_type::regular
implies appropriate<fstream>
operations would succeed, assuming no hardware, permission, access, or file system race errors. Lack offile_type::regular
does not necessarily imply<fstream>
operations would fail on a directory. — end note]- Otherwise, if the attributes indicate a directory,
as if by POSIXreturnS_ISDIR
,file_status(file_type::directory)
. [Note:file_type::directory
impliesdirectory_iterator(p)
would succeed. — end note]- Otherwise, if the attributes indicate a block special file,
as if by POSIXreturnS_ISBLK
,file_status(file_type::block)
.- Otherwise, if the attributes indicate a character special file,
as if by POSIXreturnS_ISCHR
,file_status(file_type::character)
.- Otherwise, if the attributes indicate a fifo or pipe file,
as if by POSIXreturnS_ISFIFO
,file_status(file_type::fifo)
.- Otherwise, if the attributes indicate a socket,
as if by POSIXreturnS_ISSOCK
,file_status(file_type::socket)
.- Otherwise, return
file_status(file_type::unknown)
.- Remarks: If a symbolic link is encountered during pathname resolution, pathname resolution continues using the contents of the symbolic link.
27.10.?.1.18 Symlink status [filesystem.symlink_status]
virtual file_status symlink_status(const path& p, error_code& ec) const noexcept = 0;
Copy [fs.op.symlink_status] paragraphs 1 to 4 here, with edits as specified:
- Effects: Same as status(), above, except that the attributes of
p
aredetermined as if by POSIXalways determined, even iflstat()
p
names a symbolic link.- Returns: Same as status(), above, except that if the attributes indicate a symbolic link,
as if by POSIXreturnS_ISLNK
,file_status(file_type::symlink)
.The signature with argumentReturnsec
rfile_status(file_type::none)
if an error occurs.- Remarks:Pathname resolution terminates if
p
names a symbolic link.Throws: As specified in Error reporting (27.5.6.5).
27.10.?.1.19 System complete [filesystem.system_complete]
virtual path system_complete(const path& p error_code& ec) const = 0;
Copy [fs.op.system_complete] paragraphs 1 to 6 here, with edits as specified:
- Effects: Composes an absolute path from
p
, using the same rules usedby the operating systemto resolve a path passedas the filename argument to standard library open functionsto other functions in this section.- Returns: The composed path.
The signature with argumentReturnsec
rpath()
if an error occurs.- Postcondition: For the returned path,
rp, rp.is_absolute()
is true.Throws: As specified in Error reporting (27.5.6.5).- [Example: For POSIX based operating systems,
system_complete(p)
has the same semantics asabsolute(p, current_path())
.- For Windows based operating systems,
system_complete(p)
has the same semantics asabsolute(p, current_path())
ifp.is_absolute() || !p.has_root_name()
orp
andbase
have the sameroot_name()
. Otherwise it acts likeabsolute(p, cwd)
is the current directory for thep.root_name()
drive. This will be the current directory for that drive the last time it was set, and thus may be residue left over from a prior program run by the command processor. Although these semantics are useful, they may be surprising. — end example]
27.10.?.1.20 Temp directory path [filesystem.temp_directory_path]
virtual path temp_directory_path(error_code& ec) const = 0;
Copy [fs.op.temp_dir_path] paragraphs 1 to 4 here, with edits as specified:
- Returns: An unspecifed directory path suitable for temporary files. An error shall be reported if
!exists(p) || !is_directory(p)
, wherep
is the path to be returned.The signature with argumentReturnsec
rpath()
if an error occurs.Throws: As specified in Error reporting (27.5.6.5).- [Example: For POSIX based operating systems, an implementation might return the path supplied by the first environment variable found in the list TMPDIR, TMP, TEMP, TEMPDIR, or if none of these are found,
"/tmp"
.- For Windows based operating systems, an implementation might return the path reported by the Windows
GetTempPath
API function. — end example]
27.10.?.1.21 Directory traverser [filesystem.directory_traverser]
virtual unique_ptr<directory_traverser> directory_traverser( const path& p, directory_options options, error_code& ec) const = 0;
Copy [directory_iterator.members] paragraphs 2 to 4 here, with edits as specified:
- Effects: For the directory that
p
resolves to,constructs an iteratorallocates adirectory_traverser
for the first element in a sequence ofdirectory_entry
elements representing the files in the directory, if any; otherwise theend iteratorpast-the-end value. However, ifand construction encounters an error indicating that permission to access(options & directory_options::skip_permissions_denied) != directory_options::none
p
is denied, constructs theend iteratorpast-the-end value and does not report an error.- Returns: a pointer to the allocated
directory_traverser
.Throws: As specified in Error reporting (27.5.6.5).[Note: To iterate over the current directory, usedirectory_iterator(".")
rather thandirectory_iterator("")
. — end note]
27.10.? Class
default_filesystem_impl
[class.default_filesystem_impl]Class
default_filesystem_impl
is for exposition only. An implementation is permitted to provide equivalent functionality without providing a class with this name. This class provides a default implementation of thefilesystem
interface, which is implemented by delegating to the underlying operating system.Descriptions are provided only where this class differs from the base class
filesystem
.class default_filesystem_impl : public filesystem { // exposition only public: virtual bool copy_file(const path& from, const path& to, copy_options options, error_code& ec) noexcept; virtual bool create_directory(const path& p, error_code& ec) noexcept; virtual bool create_directory(const path& p, const path& existing_p, error_code& ec) noexcept; virtual void create_directory_symlink( const path& to, const path& new_symlink, error_code& ec) noexcept; virtual void create_hard_link(const path& to, const path& new_hard_link, error_code& ec) noexcept; virtual void create_symlink(const path& to, const path& new_symlink, error_code& ec) noexcept; virtual path current_path(error_code& ec); virtual void current_path(const path& p, error_code& ec) noexcept; virtual bool equivalent(const path& p1, const path& p2, error_code& ec) noexcept; virtual uintmax_t file_size(const path& p, error_code& ec) noexcept; virtual uintmax_t hard_link_count(const path& p, error_code& ec) noexcept; virtual file_time_type last_write_time(const path& p, error_code& ec) noexcept; virtual void last_write_time(const path& p, file_time_type new_time, error_code& ec) noexcept; virtual void permissions(const path& p, perms prms, error_code& ec) noexcept; virtual path read_symlink(const path& p, error_code& ec); virtual bool remove(const path& p, error_code& ec) noexcept; virtual void rename(const path& old_p, const path& new_p, erorr_code& ec) noexcept; virtual void resize_file(const path& p, uintmax_t new_size, error_code& ec) noexcept; virtual space_info space(const path& p, error_code& ec) noexcept; virtual file_status status(const path& p, error_code& ec) noexcept; virtual file_status symlink_status(const path& p, error_code& ec) noexcept; virtual path system_complete(const path& p, error_code& ec); virtual path temp_directory_path(error_code& ec); virtual unique_ptr<directory_traverser> directory_traverser( const path& p, directory_options options, error_code& ec); };27.10.?.1
filesystem
members [default_filesystem_impl.members]27.10.?.1.1 Create directory [default_filesystem_impl.create_directory]
virtual bool create_directory(const path& p, error_code& ec) noexcept;
- Remarks: Achieves its postconditions as if by POSIX
mkdir()
with a second argument ofstatic_cast<int>(perms::all)
.27.10.?.1.2 Create directory symlink [default_filesystem_impl.create_directory_symlink]
virtual void create_directory_symlink( const path& to, const path& new_symlink, error_code& ec) noexcept;
- Remarks: Achieves its postconditions as if by POSIX
symlink()
.27.10.?.1.3 Create hard link [default_filesystem.create_hard_link]
virtual void create_hard_link(const path& to, const path& new_hard_link, error_code& ec) noexcept;
- Remarks: Achieves its postconditions as if by POSIX
link()
.27.10.?.1.4 Create symlink [default_filesystem.create_symlink]
virtual void create_symlink(const path& to, const path& new_symlink, error_code& ec) noexcept;
- Remarks: Achieves its postconditions as if by POSIX
symlink()
.27.10.?.1.5 Current path [default_filesystem.current_path]
virtual path current_path(error_code& ec);
- Remarks: Obtains the current working directory as if by POSIX
getcwd()
.virtual void current_path(const path& p, error_code& ec) noexcept;
- Remarks: Achieves its postconditions as if by POSIX
chdir()
.27.10.?.1.6 Equivalent [default_filesystem.equivalent]
virtual bool equivalent(const path& p1, const path& p2, error_code& ec) noexcept;
- Remarks: Determines equivalence of the two files as if by the values of the POSIX
stat
structure, obtained as if bystat()
for the two paths, having equalst_dev
values and equalst_ino
values.27.10.?.1.7 File size [default_filesystem.file_size]
virtual uintmax_t file_size(const path& p, error_code& ec) noexcept;
- Remarks: Determines the size as if by the value of the POSIX
stat
structure memberst_size
obtained as if by POSIXstat()
.27.10.?.1.8 Last write time[default_filesystem.last_write_time]
virtual file_time_type last_write_time(const path& p, error_code& ec) noexcept;
- Remarks: Determines the modification time as if by the value of the POSIX
stat
structure memberst_mtime
obtained as if by POSIXstat()
.virtual void last_write_time(const path& p, file_time_type new_time, error_code& ec) noexcept;
- Remarks: Sets the modification time as if by POSIX
futimens()
.27.10.?.1.9 Permissions [default_filesystem.permissions]
virtual void permissions(const path& p, perms prms, error_code& ec) noexcept;
- Remarks: Applies the effective permission bits as if by POSIX
fchmodat()
.27.10.?.1.10 Remove [default_filesystem.remove]
virtual bool remove(const path& p, error_code& ec) noexcept;
- Remarks: Removes the file as if by POSIX
remove()
.27.10.?.1.11 Rename [default_filesystem.rename]
virtual void rename(const path& old_p, const path& new_p, error_code& ec) noexcept;
- Remarks: Renames the file as if by POSIX
rename()
.27.10.?.1.12 Resize file [default_filesystem.resize_file]
virtual void resize_file(const path& p, uintmax_t new_size, error_code& ec) noexcept;
- Remarks: Achieves its postconditions as if by POSIX
truncate()
.27.10.?.1.13 Space [default_filesystem.space]
virtual space_info space(const path& p, error_code& ec) noexcept;
- Remarks: The value of the
space_info
object is determined as if by using POSIXstatvfs
to obtain a POSIXstruct statvfs
, and then multiplying itsf_blocks
,f_bfree
, andf_bavail
members by itsf_frsize
member, and assigning the results to thecapacity
,free
, andavailable
members respectively.27.10.?.1.14 Status [default_filesystem.status]
virtual file_status status(const path& p, error_code& ec) noexcept;
- Remarks: Determines the attributes as if by POSIX
stat()
.27.10.?.1.15 Symlink status [default_filesystem.symlink_status]
virtual file_status symlink_status(const path& p, error_code& ec) noexcept;
- Remarks: Determines the attributes as if by POSIX
lstat()
.27.10.? Class
directory_traverser
[class.directory_traverser]class directory_traverser { public: directory_traverser() = default; virtual ~directory_traverser() = default; virtual const directory_entry& dereference() const = 0; virtual void increment(error_code& ec) noexcept = 0; virtual bool equal(const directory_traverser& rhs) const = 0; virtual bool is_end() const noexcept = 0; virtual unique_ptr<directory_traverser> clone() const = 0; };The
directory_traverser
class defines the base class for the types of objects that represent how a directory in a particular filesystem implementation is traversed. An instance of such a type is either a past-the-end value, or points to an element of a sequence ofdirectory_entry
objects representing the contents of the directory.Classes derived from
directory_traverser
shall satisfy all of the requirements ofdirectory_traverser
.[Note: If a file is removed from or added to a directory after the construction of a
directory_traverser
for the directory, it is unspecified whether or not subsequently incrementing thedirectory_traverser
will ever result in it pointing to the removed or added directory entry. See POSIXreaddir_r
. — end note]27.10.?.1
directory_traverser
members [directory_traverser.members]virtual const directory_entry& dereference() const = 0;
- Precondition:
!is_end()
- Returns: The current element of the sequence being traversed.
virtual void increment(error_code& ec) = 0;
- Precondition:
!is_end()
- Effects: Advances
*this
to the next element of the sequence being traversed. If there is no such element, makes*this
a past-the-end value.virtual bool equal(const directory_traverser& rhs) const = 0;
- Precondition:
rhs
has the same dynamic type as*this
.- Returns:
true
ifrhs
points to the same sequence element as*this
, or if they are both past-the-end values, andfalse
otherwise.virtual bool is_end() const = 0;
- Returns:
true
if*this
represents a past-the-end value, andfalse
otherwise.virtual unique_ptr<directory_traverser> clone() const = 0;
- Returns: a value
v
such thatv->equal(*this)
(and the dynamic type of*v
is the same as the dynamic type of*this
).
Revise the directory_iterator
class synopsis as follows:
namespace std::filesystem { class directory_iterator { public: typedef directory_entry value_type; typedef ptrdiff_t difference_type; typedef const directory_entry* pointer; typedef const directory_entry& reference; typedef input_iterator_tag iterator_category; // member functions directory_iterator() noexcept; explicit directory_iterator(const path& p); directory_iterator(const path& p, directory_options options); directory_iterator(const path& p, error_code& ec) noexcept; directory_iterator(const path& p, directory_options options, error_code& ec) noexcept; directory_iterator(const directory_iterator& rhs); directory_iterator(directory_iterator&& rhs) noexcept; ~directory_iterator(); directory_iterator& operator=(const directory_iterator& rhs); directory_iterator& operator=(directory_iterator&& rhs) noexcept; const directory_entry& operator*() const; const directory_entry* operator->() const; directory_iterator& operator++(); directory_iterator& increment(error_code& ec) noexcept; // other members as required by 24.2.3, input iterators private: unique_ptr<directory_traverser> traverser; // exposition only }; }
Revise [directory_iterator.members] as follows:
27.10.13.1
directory_iterator
members [directory_iterator.members]directory_iterator() noexcept;
- Effects:
Constructs the end iterator.Initializestraverser
withnullptr
.explicit directory_iterator(const path& p); directory_iterator(const path& p, directory_options options); directory_iterator(const path& p, error_code& ec) noexcept; directory_iterator(const path& p, directory_options options, error_code& ec) noexcept;
- Effects:
For the directory thatInitializesp
resolves to, constructs an iterator for the first element in a sequence ofdirectory_entry
elements representing the files in the directory, if any; otherwise the end iterator. However, ifand construction encounters an error indicating that permission to access(options & directory_options::skip_permissions_denied) != directory_options::none
p
is denied, constructs the end iterator and does not report an error.traverser
with the result ofcurrent_filesystem.load()->directory_traverser(p, options, ec)
(withoptions
equal todirectory_options::none
if not specified by the caller). The signatures with argumentec
construct the end iterator if an error occurs.- Throws: As specified in Error reporting (27.5.6.5).
- [Note: To iterate over the current directory, use
directory_iterator(".")
rather thandirectory_iterator("")
. — end note]directory_iterator(const directory_iterator& rhs);
- Effects: Initializes
traverser
withrhs.traverser->clone()
.directory_iterator(directory_iterator&& rhs);
- Effects:
Constructs an object of classInitializesdirectory_iterator
.traverser
withstd::move(rhs.traverser)
.Postconditions:*this
has the original value ofrhs
.directory_iterator& operator=(const directory_iterator& rhs);
- Effects: If
*this
andrhs
are the same object, no effect. Otherwise, assignsrhs.traverser->clone()
totraverser
.- Returns:
*this
.directory_iterator& operator=(directory_iterator&& rhs) noexcept;
- Effects:
IfAssigns*this
andrhs
are the same object, the member has no effect.std::move(rhs.traverser)
totraverser
.Postconditions:*this
has the original value ofrhs
.- Returns:
*this
.const directory_entry& operator*() const;
- Effects: Equivalent to
return traverser->dereference();
.const directory_entry* operator->() const;
- Effects: Equivalent to
return &traverser->dereference();
.directory_iterator& operator++(); directory_iterator& increment(error_code& ec) noexcept;
- Effects:
As specified by Input iterators (24.2.3).Invokestraverser->increment()
.- Returns:
*this
.- Throws: As specified in Error reporting (27.5.6.5).
Add the following to the end of [directory_iterator.nonmembers]:
bool operator==(const directory_iterator& lhs, const directory_iterator& rhs);
- Returns: A value determined by Table ?.
Table ? — operator==(const directory_iterator&, const directory_iterator&)
return valuerhs.traverser == nullptr || rhs.traverser->is_end()
!(rhs.traverser == nullptr || rhs.traverser->is_end())
lhs.traverser == nullptr || lhs.traverser->is_end()
true
false
!(lhs.traverser == nullptr || lhs.traverser->is_end())
false
lhs.traverser->equal(*rhs.traverser)
bool operator!=(const directory_iterator& lhs, const directory_iterator& rhs);
- Effects: Equivalent to
!(lhs == rhs)
.
Insert a new section above [fs.op.funcs]:
27.10.? Filesystem injection [fs.inject]
27.10.?.1 Current filesystem [fs.inject.current]
extern atomic<filesystem*> current_filesystem;The
current_filesystem
variable is for exposition only. An implementation is permitted to provide equivalent functionality without providing a variable with this name.
current_filesystem
will be initialized to&default_filesystem()
before the body ofmain()
begins execution. [Note Implementations are encouraged to initialize before main() begins execution. --end note]27.10.?.2 Inject filesystem [fs.inject.inject]
void inject_filesystem(filesystem& fs);
- Effects: Equivalent to
current_filesystem = addressof(fs);
.- Remarks:
fs
must remain live until the next call toinject_filesystem
, and until all pending calls to filesystem operation functions have completed execution.27.10.?.3 Default filesystem [fs.inject.default]
filesystem& default_filesystem();
- Returns: An object of type
default_filesystem_impl
. All invocations of this function will return the same object, and this object will not be destroyed during program execution.
Revise [fs.op.copy] paragraph 6 as follows:
Otherwise if
is_regular_file(f)
, then:
- If
(options & copy_options::directories_only) != copy_options::none
, then return.- Otherwise if
(options & copy_options::create_symlinks) != copy_options::none
, thencreate a symbolic link to the source filecreate_symlink(from, to)
.- Otherwise if
(options & copy_options::create_hard_links) != copy_options::none
, thencreate a hard link to the source filecreate_hard_link(from, to)
.- Otherwise if
is_directory(t)
, thencopy_file(from, to/from.filename(), options)
.- Otherwise,
copy_file(from, to, options)
.
Revise [fs.op.copy_file] as follows:
bool copy_file(const path& from, const path& to); bool copy_file(const path& from, const path& to, error_code& ec) noexcept;
- Returns:
copy_file(from, to, copy_options::none)
orcopy_file(from, to, copy_options::none, ec)
, respectively.- Throws: As specified in Error reporting (27.5.6.5).
bool copy_file(const path& from, const path& to, copy_options options); bool copy_file(const path& from, const path& to, copy_options options, error_code& ec) noexcept;
Requires: At most one constant from eachcopy_options
group is present inoptions
.- Effects:
Report a file already exists error as specified in Error reporting (27.5.6.5) if:InvokesOtherwise copy the contents and attributes of the file
exists(to)
andequivalent(from, to)
, orexists(to)
and(options & (copy_options::skip_existing | copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none
.from
resolves to to the fileto
resolves to if:Otherwise no effects.
!exists(to)
, orexists(to)
and(options & copy_options::overwrite_existing) != copy_options::none
, orexists(to)
and(options & copy_options::update_existing) != copy_options::none
, andfrom
is more recent thanto
, determined as if by use of thelast_write_time
function.current_filesystem.load()->copy_file(from, to, options, ec)
and handles any errors as specified in Error reporting (27.10.7).- Returns:
The return value of thetrue
if thefrom
file was copied, otherwisefalse
. The signature with argumentec
returnfalse
if an error occurs.copy_file()
member call.- Throws: As specified in Error reporting (27.5.6.5).
Complexity: At most one direct or indirect invocation ofstatus(to)
.
Revise [fs.op.create_directory] as follows:
bool create_directory(const path& p); bool create_directory(const path& p, error_code& ec) noexcept;
- Effects:
Establishes the postcondition by attempting to create the directoryInvokesp
resolves to, as if by POSIX. Creation failure becausemkdir()
with a second argument ofstatic_cast<int>(perms::all)
p
resolves to an existing directory shall not be treated as an error.current_filesystem.load()->create_directory(p, ec)
and handles any errors as specified in Error reporting (27.10.7).Postcondition:is_directory(p)
.- Returns:
The return value of thetrue
if a new directory was created, otherwisefalse
. The signature with argumentec
returnsfalse
if an error occurs.create_directory()
member call.- Throws: As specified in Error reporting (27.5.6.5).
bool create_directory(const path& p, const path& existing_p); bool create_directory(const path& p, const path& existing_p, error_code& ec) noexcept;
- Effects:
Establishes the postcondition by attempting to create the directoryInvokesp
resolves to, with attributes copied from directoryexisting_p
. The set of attributes copied is operating system dependent. Creation failure becausep
resolves to an existing directory shall not be treated as an error. [Note: For POSIX based operating systems the attributes are those copied by native APIstat(existing_p.c_str(), &attributes_stat)
followed bymkdir(p.c_str(), attributes_stat.st_mode)
. For Windows based operating systems the attributes are those copied by native APICreateDirectoryExW(existing_p.c_str(), p.c_str(), 0)
. — end note]current_filesystem.load()->create_directory(p, existing_p, ec);
and handles any errors as specified in Error reporting (27.10.7).Postcondition:is_directory(p)
.- Returns:
The return value of thetrue
if a new directory was created, otherwisefalse
. The signature with argumentec
returns false if an error occurs.create_directory()
member call.- Throws: As specified in Error reporting (27.5.6.5).
Revise [fs.op.create_dir_symlk] as follows:
void create_directory_symlink(const path& to, const path& new_symlink); void create_directory_symlink(const path& to, const path& new_symlink, error_code& ec) noexcept;
- Effects:
Establishes the postcondition, as if by POSIXInvokessymlink()
current_filesystem.load()->create_directory_symlink(to, new_symlink, ec)
and handles any errors as specified in Error reporting (27.10.7).Postcondition:new_symlink
resolves to a symbolic link file that contains an unspecified representation ofto
.- Throws: As specified in Error reporting (27.5.6.5).
- [Note: Some operating systems require symlink creation to identify that the link is to a directory. Portable code should use
create_directory_symlink()
to create directory symlinks rather thancreate_symlink()
— end note]- [Note: Some operating systems do not support symbolic links at all or support them only for regular files. Some file systems (such as the FAT file system) do not support symbolic links regardless of the operating system. — end note]
Revise [fs.op.create_hard_lk] as follows:
void create_hard_link(const path& to, const path& new_hard_link); void create_hard_link(const path& to, const path& new_hard_link, error_code& ec) noexcept;
- Effects:
Establishes the postcondition, as if by POSIXInvokeslink()
current_filesystem.load()->create_hard_link(to, new_hard_link, ec)
and handles any errors as specified in Error reporting (27.10.7).Postcondition:
exists(to) && exists(new_hard_link) && equivalent(to, new_hard_link)
- The contents of the file or directory
to
resolves to are unchanged.- Throws: As specified in Error reporting (27.5.6.5).
- [Note: Some operating systems do not support hard links at all or support them only for regular files. Some file systems (such as the FAT file system) do not support hard links regardless of the operating system. Some file systems limit the number of links per file. — end note]
Revise [fs.op.create_symlink] as follows:
void create_symlink(const path& to, const path& new_symlink); void create_symlink(const path& to, const path& new_symlink, error_code& ec) noexcept;
- Effects:
Establishes the postcondition, as if by POSIXInvokessymlink()
current_filesystem.load()->create_symlink(to, new_symlink, ec)
and handles any errors as specified in Error reporting (27.10.7).Postcondition:new_symlink
resolves to a symbolic link file that contains an unspecified representation ofto
.- Throws: As specified in Error reporting (27.5.6.5).
- [Note: Some operating systems do not support symbolic links at all or support them only for regular files. Some file systems (such as the FAT file system) do not support symbolic links regardless of the operating system. — end note]
Revise [fs.op.current_path] as follows:
path current_path(); path current_path(error_code& ec);
- Effects: Invokes
current_filesystem.load()->current_path(ec)
and handles any errors as specified in Error reporting (27.10.7).- Returns:
The absolute path of the current working directory, obtained as if by POSIXThe return value of thegetcwd()
. The signature with argumentec
returnspath()
if an error occurs.current_path()
member call.- Throws: As specified in Error reporting (27.5.6.5).
Remarks: The current working directory is the directory, associated with the process, that is used as the starting location in pathname resolution for relative paths.- [Note: The
current_path()
name was chosen to emphasize that the return is a path, not just a single directory name.- The current path as returned by many operating systems is a dangerous global variable. It may be changed unexpectedly by a third-party or system library functions, or by another thread. — end note]
void current_path(const path& p); void current_path(const path& p, error_code& ec) noexcept;
- Effects:
Establishes the postcondition, as if by POSIXInvokeschdir()
current_filesystem.load()->current_path(p, ec)
and handles any errors as specified in Error reporting (27.10.7).Postcondition:equivalent(p, current_path())
.- Throws: As specified in Error reporting (27.5.6.5).
- [Note: The current path for many operating systems is a dangerous global state. It may be changed unexpectedly by a third-party or system library functions, or by another thread. — end note]
Revise [fs.op.equivalent] as follows:
bool equivalent(const path& p1, const path& p2); bool equivalent(const path& p1, const path& p2, error_code& ec) noexcept;
- Effects:
DeterminesInvokesfile_status s1
ands2
, as if bystatus(p1)
andstatus(p2)
, respectivelycurrent_filesystem.load()->equivalent(p1, p2, ec)
and handles any errors as specified in Error reporting (27.10.7).- Returns:
The return value of thetrue
, ifs1 == s2
andp1
andp2
resolve to the same file system entity, elsefalse
. The signature with argumentec
returnsfalse
if an error occurs.equivalent()
member call.Two paths are considered to resolve to the same file system entity if two candidate entities reside on the same device at the same location. This is determined as if by the values of the POSIXstat
structure, obtained as if bystat()
for the two paths, having equalst_dev
values and equalst_ino
values.- Throws:
As specified in Error reporting (27.5.6.5).filesystem_error
if(!exists(s1) && !exists(s2)) || (is_other(s1) && is_other(s2))
, otherwise a
Revise [fs.op.file_size] as follows:
uintmax_t file_size(const path& p); uintmax_t file_size(const path& p, error_code& ec) noexcept;
- Effects: Invokes
current_filesystem.load()->file_size(p, ec)
and handles any errors as specified in Error reporting (27.10.7).- Returns:
IfThe return value of the!exists(p) || !is_regular_file(p)
an error is reported 27.5.6.5. Otherwise, the size in bytes of the filep
resolves to, determined as if by the value of the POSIXstat
structure memberst_size
obtained as if by POSIXstat()
. The signature with argumentec
returnsstatic_cast<uintmax_t>(-1)
if an error occurs.file_size()
member call.- Throws: As specified in Error reporting (27.5.6.5).
Revise [fs.op.hard_lk_ct] as follows:
uintmax_t hard_link_count(const path& p); uintmax_t hard_link_count(const path& p, error_code& ec) noexcept;
- Effects: Invokes
current_filesystem.load()->hard_link_count(p, ec)
and handles any errors as specified in Error reporting (27.10.7).- Returns:
The number of hard links for. The signature with argumentp
ec
returnsstatic_cast<uintmax_t>(-1)
if an error occurs.The return value of thehard_link_count()
member call.- Throws: As specified in Error reporting (27.5.6.5).
Revise [fs.op.last_write_time] as follows:
file_time_type last_write_time(const path& p); file_time_type last_write_time(const path& p, error_code& ec) noexcept;
- Effects: Invokes
current_filesystem.load()->last_write_time(p, ec)
and handles any errors as specified in Error reporting (27.10.7).- Returns:
The time of last data modification ofThe return value of thep
, determined as if by the value of the POSIXstat
structure memberst_mtime
obtained as if by POSIXstat()
. The signature with argumentec
returnsfile_time_type::min()
if an error occurs.last_write_time()
member call.- Throws: As specified in Error reporting (27.5.6.5).
void last_write_time(const path& p, file_time_type new_time); void last_write_time(const path& p, file_time_type new_time, error_code& ec) noexcept;
- Effects:
Sets the time of last data modification of the file resolved to byInvokesp
tonew_time
, as if by POSIXfutimens()
current_filesystem.load()->last_write_time(p, new_time, ec)
and handles any errors as specified in Error reporting (27.10.7).- Throws: As specified in Error reporting (27.5.6.5).
- [Note: A postcondition of
last_write_time(p) == new_time
is not specified since it might not hold for file systems with coarse time granularity. — end note]
Revise [fs.op.permissions] as follows:
void permissions(const path& p, perms prms); void permissions(const path& p, perms prms, error_code& ec) noexcept;
- Requires:
!((prms & prms::add_perms) != perms::none<br> && (prms & perms::remove_perms) != perms::none)
.- Effects:
Applies the effective permissions bits fromInvokesprms
to the filep
resolves to, as if by POSIXfchmodat()
. The effective permission bits are determined as specified in Table 150.
Table 150 — Effects of permission bits Bits present in prms
Effective bits applied Neither add_perms
norremove_perms
prms && perms::mask
add_perms
status(p).permissions() | (prms & perms::mask)
remove_perms
status(p).permissions() & (prms & perms::mask)
current_filesystem.load()->permissions(p, prms, ec)
and handles any errors as specified in Error reporting (27.10.7).[Note: Conceptually permissions are viewed as bits, but the actual implementation may use some other mechanism. — end note]- Throws: As specified in Error reporting (27.5.6.5).
Revise [fs.op.read_symlink] as follows:
path read_symlink(const path& p); path read_symlink(const path& p, error_code& ec);
- Effects: Invokes
current_filesystem.load()->read_symlink(p, ec)
and handles any errors as specified in Error reporting (27.10.7).- Returns:
IfThe return value of thep
resolves to a symbolic link, apath
object containing the contents of that symbolic link. The signature with argumentec
returnspath()
if an error occurs.read_symlink()
member call.- Throws: As specified in Error reporting (27.5.6.5). [Note: It is an error if
p
does not resolve to a symbolic link. — end note]
Revise [fs.op.remove] as follows:
bool remove(const path& p); bool remove(const path& p, error_code& ec) noexcept;
- Effects:
IfInvokesexists(symlink_status(p,ec))
, it is removed as if by POSIXremove()
current_filesystem.load()->remove(p, ec)
and handles any errors as specified in Error reporting (27.10.7).[Note: A symbolic link is itself removed, rather than the file it resolves to being removed. — end note]Postcondition:!exists(symlink_status(p))
.- Returns:
The return value of thefalse
ifp
did not exist, otherwisetrue
. The signature with argumentec
returnsfalse
if an error occurs.remove()
member call- Throws: As specified in Error reporting (27.5.6.5).
Revise [fs.op.rename] as follows:
void rename(const path& old_p, const path& new_p); void rename(const path& old_p, const path& new_p, error_code& ec) noexcept;
- Effects:
RenamesInvokesold_p
tonew_p
, as if by POSIXrename()
current_filesystem.load()->rename(old_p, new_p, ec)
and handles any errors as specified in Error reporting (27.10.7).[Note: Ifold_p
andnew_p
resolve to the same existing file, no action is taken. Otherwise, ifnew_p
resolves to an existing non-directory file, it is removed, while ifnew_p
resolves to an existing directory, it is removed if empty on POSIX compliant operating systems but is an error on some other operating systems. A symbolic link is itself renamed, rather than the file it resolves to being renamed. — end note]- Throws: As specified in Error reporting (27.5.6.5).
Revise [fs.op.resize_file] as follows:
void resize_file(const path& p, uintmax_t new_size); void resize_file(const path& p, uintmax_t new_size, error_code& ec) noexcept;
- Effects: Invokes
current_filesystem.load()->resize_file(p, new_size, ec)
and handles any errors as specified in Error reporting (27.10.7).Postcondition:file_size() == new_size
.- Throws: As specified in Error reporting (27.5.6.5).
Remarks: Achieves its postconditions as if by POSIXtruncate()
.
Revise [fs.op.space] as follows:
space_info space(const path& p); space_info space(const path& p, error_code& ec) noexcept;
- Effects: Invokes
current_filesystem.load()->space(p, ec)
and handles any errors as specified in Error reporting (27.10.7).- Returns:
An object of typeThe return value of thespace_info
. The value of thespace_info
object is determined as if by using POSIXstatvfs
to obtain a POSIXstruct statvfs
, and then multiplying itsf_blocks
,f_bfree
, andf_bavail
members by itsf_frsize
member, and assigning the results to thecapacity
,free
, andavailable
members respectively. Any members for which the value cannot be determined shall be set tostatic_cast<uintmax_t>(-1)
. For the signature with argumentec
, all members are set tostatic_cast<uintmax_t>(-1)
if an error occurs.space()
member call.- Throws: As specified in Error reporting (27.5.6.5).
Remarks: The value of memberspace_info::available
is operating system dependent. [Note:available
may be less thanfree
. — end note]
Revise [fs.op.status] as follows:
file_status status(const path& p);
- Effects: As if:
error_code ec; file_status result = status(p, ec); if (result == file_type::none) throw filesystem_error(implementation-supplied-message , p, ec); return result;- Returns: See above.
- Throws:
filesystem_error
[Note:result
values offile_status(file_type::not_found)
andfile_status(file_type::unknown)
are not considered failures and do not cause an exception to be thrown. — end note]file_status status(const path& p, error_code& ec) noexcept;
- Effects:
If possible, determines the attributes of the fileEquivalent top
resolves to, as if by POSIXstat()
. If, during attribute determination, the underlying file system API reports an error, setsec
to indicate the specific error reported. Otherwise,ec.clear()
.return current_filesystem.load()->status(p, ec);
.[Note: This allows users to inspect the specifics of underlying API errors even when the value returned bystatus()
is notfile_status(file_type::none)
. — end note]Returns: Ifec != error_code()
:
- If the specific error indicates that
p
cannot be resolved because some element of the path does not exist, returnfile_status(file_type::not_found)
.- Otherwise, if the specific error indicates that
p
can be resolved but the attributes cannot be determined, returnfile_status(file_type::unknown)
.- Otherwise, return
file_status(file_type::none)
.[Note: These semantics distinguish between
p
being known not to exist,p
existing but not being able to determine its attributes, and there being an error that prevents even knowing ifp
exists. These distinctions are important to some use cases. — end note]Otherwise,
- If the attributes indicate a regular file, as if by POSIX
S_ISREG
, returnfile_status(file_type::regular)
. [Note:file_type::regular
implies appropriate<fstream>
operations would succeed, assuming no hardware, permission, access, or file system race errors. Lack offile_type::regular
does not necessarily imply<fstream>
operations would fail on a directory. — end note]- Otherwise, if the attributes indicate a directory, as if by POSIX
S_ISDIR
, returnfile_status(file_type::directory)
. [Note:file_type::directory
impliesdirectory_iterator(p)
would succeed. — end note]- Otherwise, if the attributes indicate a block special file, as if by POSIX
S_ISBLK
, returnfile_status(file_type::block)
.- Otherwise, if the attributes indicate a character special file, as if by POSIX
S_ISCHR
, returnfile_status(file_type::character)
.- Otherwise, if the attributes indicate a fifo or pipe file, as if by POSIX
S_ISFIFO
, returnfile_status(file_type::fifo)
.- Otherwise, if the attributes indicate a socket, as if by POSIX
S_ISSOCK
, returnfile_status(file_type::socket)
.- Otherwise, return
file_status(file_type::unknown)
.Remarks: If a symbolic link is encountered during pathname resolution, pathname resolution continues using the contents of the symbolic link.
Revise [fs.op.symlink_status] as follows:
file_status symlink_status(const path& p);
- Effects: As if:
error_code ec; file_status result = status_symlink(p, ec); if (result == file_type::none) throw filesystem_error(implementation-supplied-message, p, ec); return result;- Returns: See above.
- Throws:
filesystem_error
[Note:result
values offile_status(file_type::not_found)
andfile_status(file_type::unknown)
are not considered failures and do not cause an exception to be thrown. — end note]file_status symlink_status(const path& p, error_code& ec) noexcept;
- Effects:
Same as status(), above, except that the attributes ofEquivalent top
are determined as if by POSIXlstat()
return current_filesystem.load()->symlink_status(p, ec)
.Returns: Same as status(), above, except that if the attributes indicate a symbolic link, as if by POSIXS_ISLNK
, returnfile_status(file_type::symlink)
The signature with argumentec
returnsfile_status(file_type::none)
if an error occurs.Remarks: Pathname resolution terminates ifp
names a symbolic link.Throws: As specified in Error reporting (27.5.6.5).
Revise [fs.op.system_complete] as follows:
path system_complete(const path& p); path system_complete(const path& p, error_code& ec);
- Effects:
Composes an absolute path fromInvokesp
, using the same rules used by the operating system to resolve a path passed as the filename argument to standard library open functionscurrent_filesystem.load()->system_complete(p, ec)
and handles any errors as specified in Error reporting (27.10.7).- Returns:
The composed path. The signature with argumentThe return value of theec
returnspath()
if an error occurs.system_complete()
member call.Postcondition: For the returned path,rp
,rp.is_absolute()
is true.- Throws: As specified in Error reporting (27.5.6.5).
[Example: For POSIX based operating systems,system_complete(p)
has the same semantics asabsolute(p, current_path())
.For Windows based operating systems,system_complete(p)
has the same semantics asabsolute(p, current_path())
ifp.is_absolute() || !p.has_root_name()
orp
andbase
have the sameroot_name()
. Otherwise it acts likeabsolute(p, cwd)
is the current directory for thep.root_name()
drive. This will be the current directory for that drive the last time it was set, and thus may be residue left over from a prior program run by the command processor. Although these semantics are useful, they may be surprising. — end example]
Revise [fs.op.temp_dir_path] as follows:
path temp_directory_path(); path temp_directory_path(error_code& ec);
- Effects: Invokes
current_filesystem.load()->temp_directory_path(ec)
and handles any errors as specified in Error reporting (27.10.7).- Returns:
An unspecifed directory path suitable for temporary files. An error shall be reported ifThe return value of the!exists(p) || !is_directory(p)
, wherep
is the path to be returned. The signature with argumentec
returnspath()
if an error occurs.temp_directory_path()
member call.- Throws: As specified in Error reporting (27.5.6.5).
[Example: For POSIX based operating systems, an implementation might return the path supplied by the first environment variable found in the list TMPDIR, TMP, TEMP, TEMPDIR, or if none of these are found,"/tmp"
.For Windows based operating systems, an implementation might return the path reported by the WindowsGetTempPath
API function. — end note]