C++ Boost copy_file exception on Ubuntu

686 views Asked by At

I recently upgraded Boost from 1.61 to 1.78 (gcc 7.5.0 on an Ubuntu 18.04 VirtualBox VM) and there appears to a change in the behaviour of filesystem::copy_file.

Now, when I copy a read-only file to a directory with full permissions, I get a filesystem_error exception with detail:

boost::filesystem::copy_file: Operation not permitted [system:1]

Despite this, the read-only file is always successfully copied! Interestingly, the destination file is not read-only. In 1.61 the copy behaviour was the same but there was no exception.

This simple program reproduces the issue:

#include <boost/filesystem.hpp>
#include <string>
#include <iostream>

int main()
{
    // copyFrom should be write protected
    boost::filesystem::path copyFrom("./files/myfile.txt");
    boost::filesystem::path copyTo("./files/myfile2.txt");
    
    try
    {
        boost::filesystem::copy_file(copyFrom, copyTo, boost::filesystem::copy_options::overwrite_existing);
    }
    catch (const boost::filesystem::filesystem_error& e) {
        std::cout << "Failed to copy " << copyFrom.string() << " to " << copyTo.string() << std::endl
            << "Exception: " << e.what() << std::endl 
            << "Error code: " << e.code().message() << std::endl
            << "Error code value: " << e.code().value() << std::endl;
        return -1;
    }
    std::cout << "Copy success!" << std::endl;
    return 0;
}

Compile:

gcc -c -x c++ /home/code/copyfile/copyfile.cpp -I /home/code/Boost/1.78.0/lib/native/include -g1 -o "/home/code/copyfile/copyfile.o" -Wall -O2 -DNDEBUG -std=c++11

Link:

g++ -o "/home/code/copyfile/copyfile.out" -L/home/code/Boost/1.78.0/release/lib /home/code/copyfile/copyfile.o "-lboost_filesystem"

Output:

Failed to copy ./files/myfile.txt to ./files/myfile2.txt
Exception: boost::filesystem::copy_file: Operation not permitted [system:1]: "./files/myfile.txt", "./files/myfile2.txt"
Error code: Operation not permitted
Error code value: 1

Removing the read-only flag on the source file results in the copy succeeding with no exception, as does running the command elevated (sudo /.copyfile.out).

This seems like unusual behaviour - I am not modifying the source file in any way, so why does the exception occur?

Removing and later adding back the read-only flag on the source file feels like a hack. This code will often run over directories where there will be many read-only files.

If the exception code returned ("1") refers to the fact that the read-only state of the source file was not preserved on the copied file (for me this doesn't matter), I could use this value to decide whether to continue. Either way it seems strange because this didn't happen in the older Boost.

EDIT: strace output as per comment:

openat(AT_FDCWD, "./files/myfile.txt", O_RDONLY|O_CLOEXEC) = 3
statx(3, "", AT_STATX_SYNC_AS_STAT|AT_NO_AUTOMOUNT|AT_EMPTY_PATH, STATX_TYPE|STATX_MODE|STATX_INO|STATX_SIZE, {stx_mask=STATX_ALL, stx_attributes=0, stx_mode=S_IFREG|0555, stx_size=9, ...}) = 0
openat(AT_FDCWD, "./files/myfile2.txt", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0100755) = 4
statx(4, "", AT_STATX_SYNC_AS_STAT|AT_NO_AUTOMOUNT|AT_EMPTY_PATH, STATX_TYPE|STATX_MODE|STATX_INO, {stx_mask=STATX_ALL, stx_attributes=0, stx_mode=S_IFREG|0777, stx_size=0, ...}) = 0
fstatfs(3, {f_type=0x786f4256, f_bsize=4096, f_blocks=244189951, f_bfree=48066312, f_bavail=48066312, f_files=1000, f_ffree=1000000, f_fsid={val=[0, 0]}, f_namelen=255, f_frsize=4096, f_flags=ST_VALID|ST_NODEV|ST_RELATIME}) = 0
copy_file_range(3, NULL, 4, NULL, 9, 0) = 9
fchmod(4, 0100555)                      = -1 EPERM (Operation not permitted)
close(4)                                = 0
close(3)                                = 0
0

There are 0 answers