|Reply-to||Jonathan Wakely <email@example.com>|
Historically, C++ iostreams libraries had a
noreplace open mode that
corresponded to the
O_EXCL flag for POSIX
That mode was not included in the C++98 standard,
presumably for portability reasons, because it wasn't in ISO C90.
Since then, ISO C added support for "exclusive" mode to
so now C++'s
<fstream> is missing a feature that is present in both
ISO C and POSIX. We should fix this for C++23.
C11 added an 'x' modifier to the
fopen flags for files opened in write mode.
This opens the file in "exclusive" mode, meaning the
fopen call fails if the
file already exists.
This is quite an important feature for certain use cases.
"This is necessary to eliminate
a time-of-creation to time-of-use race condition vulnerability. [...]
fopen() does not indicate if an existing file has been opened for writing
or a new file has been created.
This may lead to a program overwriting or accessing an unintended file."
See N1339 for additional rationale.
C++ already incorporates the C11 changes to
fopen by reference,
std::fstream has no way to achieve the same thing.
To avoid the time-of-creation to time-of-use (TOCTTOU) problem
it's necessary to use
(or non-standard APIs to hand an existing file handle or file descriptor
to an fstream).
The 'x' modifier is widely supported. It was already supported as an extension by Glibc's
(since at least version 2.4 from 2006),
and is in the draft for the next revision of POSIX
(because it's rebasing on C11).
Support for opening an ofstream in exclusive mode isn't even a new idea,
pre-ISO iostreams provided it. References to a
noreplace flag can be
found in texts such as:
The flag is still present in the MSVC library, as
and in the Apache stdcxx implementation, as
The historical name was "noreplace" but we could consider something like
ios_base::excl to correspond more closely with POSIX and C. I originally
preferred that, but have since decided that it's better to be consistent
with the historical
noreplace name, which is still present as
ios_base::_Noreplace in MSVC. I think that the meaning of "noreplace"
is a bit more intuitable than "exclusive". If you don't already know what
POSIX or C means by "exclusive mode" then the name doesn't help you.
We could also consider not using an
ios_base::openmode for this,
but just add new constructors to
to request the file be opened in exclusive mode. This would be novel,
as all existing options for opening files (such as binary mode) are done
openmode flags. There was no interest in doing it any differently when
the idea was discussed on the LEWG reflector.
Niall Douglas raised a concern related to the ISO C specification for
which is vague about what "exclusive" mode means, and allows it to be ignored.
I feel we should not deviate from C, so any fixes should be done "upstream"
in the C standard. That makes it simpler to implement the C++ feature in terms
fopen, rather than having to do use OS-specific APIs.
This is relative to the N4901 working draft.
Add a feature test macro to [version.syn]:
Add a new
openmode constant to the
ios_base synopsis in [ios.base.general]
// [ios.openmode], openmode using openmode = T3; static constexpr openmode app = unspecified; static constexpr openmode ate = unspecified; static constexpr openmode binary = unspecified; static constexpr openmode in = unspecified; static constexpr openmode out = unspecified; static constexpr openmode trunc = unspecified;
Add a new row to Table 123 [tag:ios.openmode]:
Element Effect(s) if set
seek to end before each write
open and seek to end immediately after opening
perform input and output in binary mode (as opposed to text mode)
open for input
open for output
truncate an existing stream when opening
Add a new column to Table 130 [tab:filebuf.open.modes], as indicated:
+ "w" + + "w" + + "a" + "a" + "r" + + "r+" + + + "w+" + + + "a+" + + "a+" + + "wb" + + + + "wb" + + + + "ab" + + "ab" + + "rb" + + + "r+b" + + + + "w+b" + + + + "a+b" + + + "a+b"