https://gcc.gnu.org/bugzilla/show_bug.cgi?id=122726
Bug ID: 122726
Summary: std::filesystem::rename() on Windows reports
errc::io_error on any MoveFileExW error except
ERROR_ACCESS_DENIED
Product: gcc
Version: 14.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: libstdc++
Assignee: unassigned at gcc dot gnu.org
Reporter: daniel.plakhotich at gmail dot com
Target Milestone: ---
std::filesystem::rename() on Windows does not report errors properly, because
any error code except ERROR_ACCESS_DENIED from the underlying MoveFileExW()
gets mapped to std::errc::io_error.
The issue was introduced in
https://gcc.gnu.org/pipermail/libstdc++-cvs/2021q1/035473.html
As you can see, the error handling in posix::rename() (used by the public
filesystem::rename()) only handles ERROR_ACCESS_DENIED (mapped to EACCES), and
everything else is mapped to EIO (and eventually to std::errc::io_error):
if (GetLastError() == ERROR_ACCESS_DENIED)
errno = EACCES;
else
errno = EIO;
Is it possible to create a more complete mapping from GetLastError() to
std::errc to make rename() errors more useful? As far as I can tell, the other
functions from the filesystem module - or at least the ones I use - don't have
this issue. For example, file_size() and resize_file() correctly return
std::errc::no_such_file_or_directory when called for a nonexistent file.
A minimal example to reproduce the issue:
#include <cassert>
#include <filesystem>
#include <iostream>
#include <system_error>
int main()
{
std::error_code ec;
std::filesystem::rename("nonexistent", "new-name", ec);
assert(ec == std::errc::io_error);
std::cout << ec.message() << " | " << ec << '\n';
}