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';
}

Reply via email to