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_codeparameter is specified to call a function that takes anerror_codeparameter, the outer function is understood to construct a localerror_codevariableecto pass to the function call. If!ecafter the call completes, unless otherwise specified the function throws afilesystem_errorexception 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
filesystemdefines the base class for the types of objects that provide access to the fundamental operations of a specific file system. A specialfilesystemobject ([class.default_filesystem_impl]) provides access to the native file system provided by the operating system, and otherfilesystemobjects can implement virtual file systems for purposes such as testing.Classes derived from
filesystemshall 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
filesystemmembers [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_optionsgroup 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.fromresolves to to the filetoresolves 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, andfromis more recent thanto, determined as if by use of thelast_write_timefunction.- Returns:
trueif thefromfile was copied, otherwisefalse.The signature with argumentReturnecrfalseif 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
presolves to, as if by POSIX. Creation failure becausemkdir()with a second argument ofstatic_cast<int>(perms::all)presolves to an existing directory shall not be treated as an error.- Postcondition:
is_directory(p).- Returns:
trueif a new directory was created, otherwisefalse.The signature with argumentReturnsecrfalseif 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
presolves to, with attributes copied from directoryexisting_p. The set of attributes copied is operating system dependent. Creation failure becausepresolves 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:
trueif a new directory was created, otherwisefalse.The signature with argumentReturns false if an error occurs.ecrThrows: 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_symlinkresolves 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
toresolves 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_symlinkresolves 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 argumentReturnsecrpath()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 s1ands2, as if bystatus(p1)andstatus(p2), respectively.- Returns:
true, ifs1 == s2andp1andp2resolve to the same file system entity, elsefalse.The signature with argumentReturnsecrfalseif 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 POSIXstatstructure, obtained as if bystat()for the two paths, having equalst_devvalues and equalst_inovalues.Throws:filesystem_errorif(!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 filepresolves to, determined as if by the value of the POSIX.statstructure memberst_sizeobtained as if by POSIXstat()The signature with argumentReturnsecrstatic_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 argumentReturnsecrstatic_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.statstructure memberst_mtimeobtained as if by POSIXstat()The signature with argumentReturnsecrfile_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
ptonew_time, as if by POSIX.futimens()Throws: As specified in Error reporting (27.5.6.5).[Note: A postcondition oflast_write_time(p) == new_timeis 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
prmsto the filepresolves 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 prmsEffective bits applied Neither add_permsnorremove_permsprms && perms::maskadd_permsstatus(p).permissions() | (prms & perms::mask)remove_permsstatus(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
presolves to a symbolic link, apathobject containing the contents of that symbolic link.The signature with argumentecreturnspath()if an error occurs.Throws: As specified in Error reporting (27.5.6.5).[Note: It is an error ifpdoes 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:
falseifpdid not exist, otherwisetrue.The signature with argumentReturnsecrfalseif 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_ptonew_p, as if by POSIX.rename()- [Note: If
old_pandnew_presolve to the same existing file, no action is taken. Otherwise, ifnew_presolves to an existing non-directory file, it is removed, while ifnew_presolves 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_infoobject is determined as if by using POSIXstatvfsto obtain a POSIXstruct statvfs, and then multiplying itsf_blocks,f_bfree, andf_bavailmembers by itsf_frsizemember, and assigning the results to thecapacity,free, andavailablemembers respectively.Any members for which the value cannot be determined shall be set to
capacitywill be set to the total storage capacity of the storage volume containingp.freewill be set to the total amount of free space on the storage volume containingp.availablewill 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 thanfreebecause 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::availableis operating system dependent. [Note:availablemay 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
presolves to, as if by POSIX. If, during attribute determination, the underlying file system API reports an error, setsstat()ecto 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
pcannot be resolved because some element of the path does not exist, returnfile_status(file_type::not_found).- Otherwise, if the specific error indicates that
pcan 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
pbeing known not to exist,pexisting but not being able to determine its attributes, and there being an error that prevents even knowing ifpexists. 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::regularimplies appropriate<fstream>operations would succeed, assuming no hardware, permission, access, or file system race errors. Lack offile_type::regulardoes 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::directoryimpliesdirectory_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
paredetermined as if by POSIXalways determined, even iflstat()pnames 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 argumentReturnsecrfile_status(file_type::none)if an error occurs.- Remarks:Pathname resolution terminates if
pnames 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 argumentReturnsecrpath()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()orpandbasehave 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), wherepis the path to be returned.The signature with argumentReturnsecrpath()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
GetTempPathAPI 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
presolves to,constructs an iteratorallocates adirectory_traverserfor the first element in a sequence ofdirectory_entryelements 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::nonepis 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_implis 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 thefilesysteminterface, 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
filesystemmembers [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
statstructure, obtained as if bystat()for the two paths, having equalst_devvalues and equalst_inovalues.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
statstructure memberst_sizeobtained 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
statstructure memberst_mtimeobtained 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_infoobject is determined as if by using POSIXstatvfsto obtain a POSIXstruct statvfs, and then multiplying itsf_blocks,f_bfree, andf_bavailmembers by itsf_frsizemember, and assigning the results to thecapacity,free, andavailablemembers 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_traverserclass 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_entryobjects representing the contents of the directory.Classes derived from
directory_traversershall 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_traverserfor the directory, it is unspecified whether or not subsequently incrementing thedirectory_traverserwill ever result in it pointing to the removed or added directory entry. See POSIXreaddir_r. — end note]27.10.?.1
directory_traversermembers [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
*thisto the next element of the sequence being traversed. If there is no such element, makes*thisa past-the-end value.virtual bool equal(const directory_traverser& rhs) const = 0;
- Precondition:
rhshas the same dynamic type as*this.- Returns:
trueifrhspoints to the same sequence element as*this, or if they are both past-the-end values, andfalseotherwise.virtual bool is_end() const = 0;
- Returns:
trueif*thisrepresents a past-the-end value, andfalseotherwise.virtual unique_ptr<directory_traverser> clone() const = 0;
- Returns: a value
vsuch thatv->equal(*this)(and the dynamic type of*vis 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_iteratormembers [directory_iterator.members]directory_iterator() noexcept;
- Effects:
Constructs the end iterator.Initializestraverserwithnullptr.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 thatInitializespresolves to, constructs an iterator for the first element in a sequence ofdirectory_entryelements 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::nonepis denied, constructs the end iterator and does not report an error.traverserwith the result ofcurrent_filesystem.load()->directory_traverser(p, options, ec)(withoptionsequal todirectory_options::noneif not specified by the caller). The signatures with argumentecconstruct 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
traverserwithrhs.traverser->clone().directory_iterator(directory_iterator&& rhs);
- Effects:
Constructs an object of classInitializesdirectory_iterator.traverserwithstd::move(rhs.traverser).Postconditions:*thishas the original value ofrhs.directory_iterator& operator=(const directory_iterator& rhs);
- Effects: If
*thisandrhsare the same object, no effect. Otherwise, assignsrhs.traverser->clone()totraverser.- Returns:
*this.directory_iterator& operator=(directory_iterator&& rhs) noexcept;
- Effects:
IfAssigns*thisandrhsare the same object, the member has no effect.std::move(rhs.traverser)totraverser.Postconditions:*thishas 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()truefalse!(lhs.traverser == nullptr || lhs.traverser->is_end())falselhs.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_filesystemvariable is for exposition only. An implementation is permitted to provide equivalent functionality without providing a variable with this name.
current_filesystemwill 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:
fsmust 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_optionsgroup 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.fromresolves to to the filetoresolves 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, andfromis more recent thanto, determined as if by use of thelast_write_timefunction.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 thetrueif thefromfile was copied, otherwisefalse. The signature with argumentecreturnfalseif 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 directoryInvokespresolves to, as if by POSIX. Creation failure becausemkdir()with a second argument ofstatic_cast<int>(perms::all)presolves 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 thetrueif a new directory was created, otherwisefalse. The signature with argumentecreturnsfalseif 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 directoryInvokespresolves to, with attributes copied from directoryexisting_p. The set of attributes copied is operating system dependent. Creation failure becausepresolves 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 thetrueif a new directory was created, otherwisefalse. The signature with argumentecreturns 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_symlinkresolves 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
toresolves 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_symlinkresolves 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 argumentecreturnspath()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 s1ands2, 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 == s2andp1andp2resolve to the same file system entity, elsefalse. The signature with argumentecreturnsfalseif 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 POSIXstatstructure, obtained as if bystat()for the two paths, having equalst_devvalues and equalst_inovalues.- Throws:
As specified in Error reporting (27.5.6.5).filesystem_errorif(!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 filepresolves to, determined as if by the value of the POSIXstatstructure memberst_sizeobtained as if by POSIXstat(). The signature with argumentecreturnsstatic_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 argumentpecreturnsstatic_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 POSIXstatstructure memberst_mtimeobtained as if by POSIXstat(). The signature with argumentecreturnsfile_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 byInvokesptonew_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_timeis 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 fromInvokesprmsto the filepresolves 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 prmsEffective bits applied Neither add_permsnorremove_permsprms && perms::maskadd_permsstatus(p).permissions() | (prms & perms::mask)remove_permsstatus(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 thepresolves to a symbolic link, apathobject containing the contents of that symbolic link. The signature with argumentecreturnspath()if an error occurs.read_symlink()member call.- Throws: As specified in Error reporting (27.5.6.5). [Note: It is an error if
pdoes 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 thefalseifpdid not exist, otherwisetrue. The signature with argumentecreturnsfalseif 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_ptonew_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_pandnew_presolve to the same existing file, no action is taken. Otherwise, ifnew_presolves to an existing non-directory file, it is removed, while ifnew_presolves 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_infoobject is determined as if by using POSIXstatvfsto obtain a POSIXstruct statvfs, and then multiplying itsf_blocks,f_bfree, andf_bavailmembers by itsf_frsizemember, and assigning the results to thecapacity,free, andavailablemembers 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::availableis operating system dependent. [Note:availablemay 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:resultvalues 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 topresolves to, as if by POSIXstat(). If, during attribute determination, the underlying file system API reports an error, setsecto 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
pcannot be resolved because some element of the path does not exist, returnfile_status(file_type::not_found).- Otherwise, if the specific error indicates that
pcan 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
pbeing known not to exist,pexisting but not being able to determine its attributes, and there being an error that prevents even knowing ifpexists. 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::regularimplies appropriate<fstream>operations would succeed, assuming no hardware, permission, access, or file system race errors. Lack offile_type::regulardoes 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::directoryimpliesdirectory_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:resultvalues 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 topare 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 argumentecreturnsfile_status(file_type::none)if an error occurs.Remarks: Pathname resolution terminates ifpnames 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 theecreturnspath()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()orpandbasehave 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), wherepis the path to be returned. The signature with argumentecreturnspath()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 WindowsGetTempPathAPI function. — end note]