MSYS2-packages icon indicating copy to clipboard operation
MSYS2-packages copied to clipboard

g++ 9.3.0 throw after trying to overwrite with std::filesystem::copy_options::overwrite_existing

Open diffsetter opened this issue 4 years ago • 13 comments

The following program throws if the regular file "bar" already exist with

terminate called after throwing an instance of 'std::filesystem::__cxx11::filesystem_error'
  what():  filesystem error: cannot copy file: File exists [foo] [bar]

This does not happen under linux and also not under cygwin as I have been told. So I guess it could be an msys2 issue.

#include <filesystem>
int main() {
    std::filesystem::copy_file("foo","bar",std::filesystem::copy_options::overwrite_existing);
}

If "bar" does not exist yet, the copying succeeds. I use an up to date msys2 64bit system.

diffsetter avatar May 01 '20 06:05 diffsetter

MSYS2 gcc 10.1.0, bug reproduced

o-kos avatar May 18 '20 20:05 o-kos

Yeah, I'm getting the same bug, 10.1.0, regardless of copy_options passed.

ckravatz avatar May 20 '20 18:05 ckravatz

Similar error with copy (msys2/g++ 10.2.0):

#include <filesystem>
int main() {
    std::filesystem::copy("fooDir", "barDir", std::filesystem::copy_options::overwrite_existing | std::filesystem::copy_options::recursive);
    return 0;
}

If "barDir" does not exist yet, the copying succeeds. Otherwise, I've got this error:

terminate called after throwing an instance of 'std::filesystem::__cxx11::filesystem_error' what(): filesystem error: cannot copy: File exists [fooDir] [barDir]

petitg1987 avatar Dec 26 '20 09:12 petitg1987

did anyone already solve this problem?

lucafei avatar May 10 '21 09:05 lucafei

Hi,

Same issue with msys2/g++ 10.3.0:

$ g++ --version g++.exe (Rev5, Built by MSYS2 project) 10.3.0

Can we expect a fix or do we need to work around this issue?

Thanks, Jonatan

JonatanAntoni avatar Sep 14 '21 12:09 JonatanAntoni

Hi i debugged this problem and it looks like it's upstream bug in gcc libstdc++. in file \gcc-11.2.0\libstdc++-v3\src\filesystem\ops-common.h line 376 in procedure do_copy_file they define following condition:

if (to_st->st_dev == from_st->st_dev  && to_st->st_ino == from_st->st_ino)
{
    ec = std::make_error_code(std::errc::file_exists);
    return false;
}

st_ino members are taken from _wstat64 function, but on windows it always returns 0 as linux file inodes has no meaning on windows. As explained here https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/stat-functions?view=msvc-170 . So for any two different files on same drive this condition will be true. They should use GetFileInformationByHandle instead, or disable checking this condition on windows. Sample code included below.

#include <cstring>
#include <iostream>
#include <fstream>
#include <filesystem>

// copied from from C:\M\mingw-w64-gcc\src\gcc-11.2.0\libstdc++-v3\src\filesystem\ops-common.h
// just changed namespace name __gnu_posix to my_posix
namespace my_posix {
  typedef struct ::__stat64 stat_type;

  inline int stat(const wchar_t* path, stat_type* buffer)
    { return ::_wstat64(path, buffer); }
}

int main()
{
    #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
    std::cout << "Have _GLIBCXX_FILESYSTEM_IS_WINDOWS \n";
    #endif
    #ifdef _GLIBCXX_HAVE_SYS_STAT_H
    std::cout << "Have _GLIBCXX_HAVE_SYS_STAT_H \n";
    #endif
    #if _UNICODE
    std::cout << "Have _UNICODE \n";
    #endif

    // create test files 
    std::wstring filename1 = L"file1.txt";
    std::ofstream f;

    f.open(filename1.c_str());
    if(f.is_open()) {
        f << "file1";
        f.close();
    }
    std::wstring filename2 = L"file2.txt";
    f.open(filename2.c_str());
    if(f.is_open()) {
        f << "file2....";
        f.close();
    }

    // check for bug in msys where overwritting existing file fails
    std:std::error_code ec;

    std::filesystem::copy_file(filename1,filename2,std::filesystem::copy_options::overwrite_existing, ec);
    std::cout << "copy_file returned: " << ec.message() << "\n";

    my_posix::stat_type st1, st2;
    int rv1 = my_posix::stat(filename1.c_str(), &st1);
    int rv2 = my_posix::stat(filename2.c_str(), &st2);
    if (rv1 || rv2) {
        std::cout << "stat returned error!\n";
        return 1;
    }

    // same condition as in \gcc-11.2.0\libstdc++-v3\src\filesystem\ops.cc line 376
    if (st2.st_dev == st1.st_dev
	    && st2.st_ino == st1.st_ino) {
	    std::cout << "Error:  do_copy_file considers files as same file\n";
    }
    else std::cout << "All ok. Files are not considered identical\n";
}

codeSilverWolf avatar Dec 29 '21 17:12 codeSilverWolf

This bug still exists in latest MSYS2 as of 3/15/2022.

maraakate avatar Mar 15 '22 20:03 maraakate

Still happened. :(

Nambers avatar Jul 21 '22 08:07 Nambers

This bug still exists in MSYS2 build as of 9/15/2022.

dhbloo avatar Sep 15 '22 14:09 dhbloo

Can you provide any sample code? Are you compiling for native Windows program? If yes you should use mingw gcc toolchain.

Biswa96 avatar Sep 15 '22 14:09 Biswa96