Fix error handling in copy_file and equivalent
* src/filesystem/ops.cc (do_copy_file): Report an error if source or destination is not a regular file (LWG 2712). (equivalent): Fix error handling and result when only one file exists. * testsuite/experimental/filesystem/operations/copy.cc: Remove files created by tests. Test copying directories. * testsuite/experimental/filesystem/operations/copy_file.cc: Remove files created by tests. * testsuite/experimental/filesystem/operations/equivalent.cc: New. * testsuite/experimental/filesystem/operations/is_empty.cc: New. * testsuite/experimental/filesystem/operations/read_symlink.cc: Remove file created by test. * testsuite/experimental/filesystem/operations/remove_all.cc: New. * testsuite/util/testsuite_fs.h (~scoped_file): Only try to remove file if path is non-empty, to support removal by other means. From-SVN: r241521
This commit is contained in:
parent
1cf1719bc0
commit
ec04aad76d
@ -1,3 +1,20 @@
|
||||
2016-10-25 Jonathan Wakely <jwakely@redhat.com>
|
||||
|
||||
* src/filesystem/ops.cc (do_copy_file): Report an error if source or
|
||||
destination is not a regular file (LWG 2712).
|
||||
(equivalent): Fix error handling and result when only one file exists.
|
||||
* testsuite/experimental/filesystem/operations/copy.cc: Remove files
|
||||
created by tests. Test copying directories.
|
||||
* testsuite/experimental/filesystem/operations/copy_file.cc: Remove
|
||||
files created by tests.
|
||||
* testsuite/experimental/filesystem/operations/equivalent.cc: New.
|
||||
* testsuite/experimental/filesystem/operations/is_empty.cc: New.
|
||||
* testsuite/experimental/filesystem/operations/read_symlink.cc: Remove
|
||||
file created by test.
|
||||
* testsuite/experimental/filesystem/operations/remove_all.cc: New.
|
||||
* testsuite/util/testsuite_fs.h (~scoped_file): Only try to remove
|
||||
file if path is non-empty, to support removal by other means.
|
||||
|
||||
2016-10-24 Jonathan Wakely <jwakely@redhat.com>
|
||||
|
||||
* src/filesystem/ops.cc (is_empty): Fix error handling.
|
||||
|
@ -350,6 +350,8 @@ namespace
|
||||
from_st = &st2;
|
||||
}
|
||||
f = make_file_status(*from_st);
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
// 2712. copy_file() has a number of unspecified error conditions
|
||||
if (!is_regular_file(f))
|
||||
{
|
||||
ec = std::make_error_code(std::errc::not_supported);
|
||||
@ -360,8 +362,13 @@ namespace
|
||||
|
||||
if (exists(t))
|
||||
{
|
||||
if (!is_other(t) && !is_other(f)
|
||||
&& to_st->st_dev == from_st->st_dev
|
||||
if (!is_regular_file(t))
|
||||
{
|
||||
ec = std::make_error_code(std::errc::not_supported);
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
@ -912,7 +919,7 @@ fs::equivalent(const path& p1, const path& p2)
|
||||
{
|
||||
error_code ec;
|
||||
auto result = equivalent(p1, p2, ec);
|
||||
if (ec.value())
|
||||
if (ec)
|
||||
_GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
|
||||
p1, p2, ec));
|
||||
return result;
|
||||
@ -922,25 +929,42 @@ bool
|
||||
fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
|
||||
{
|
||||
#ifdef _GLIBCXX_HAVE_SYS_STAT_H
|
||||
int err = 0;
|
||||
file_status s1, s2;
|
||||
stat_type st1, st2;
|
||||
if (::stat(p1.c_str(), &st1) == 0 && ::stat(p2.c_str(), &st2) == 0)
|
||||
if (::stat(p1.c_str(), &st1) == 0)
|
||||
s1 = make_file_status(st1);
|
||||
else if (is_not_found_errno(errno))
|
||||
s1.type(file_type::not_found);
|
||||
else
|
||||
err = errno;
|
||||
|
||||
if (::stat(p2.c_str(), &st2) == 0)
|
||||
s2 = make_file_status(st2);
|
||||
else if (is_not_found_errno(errno))
|
||||
s2.type(file_type::not_found);
|
||||
else
|
||||
err = errno;
|
||||
|
||||
if (exists(s1) && exists(s2))
|
||||
{
|
||||
file_status s1 = make_file_status(st1);
|
||||
file_status s2 = make_file_status(st2);
|
||||
if (is_other(s1) && is_other(s2))
|
||||
{
|
||||
ec = std::make_error_code(std::errc::not_supported);
|
||||
return false;
|
||||
}
|
||||
ec.clear();
|
||||
if (is_other(s1) || is_other(s2))
|
||||
return false;
|
||||
return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
|
||||
}
|
||||
else if (is_not_found_errno(errno))
|
||||
{
|
||||
ec = std::make_error_code(std::errc::no_such_file_or_directory);
|
||||
return false;
|
||||
}
|
||||
ec.assign(errno, std::generic_category());
|
||||
else if (!exists(s1) && !exists(s2))
|
||||
ec = std::make_error_code(std::errc::no_such_file_or_directory);
|
||||
else if (err)
|
||||
ec.assign(err, std::generic_category());
|
||||
else
|
||||
ec.clear();
|
||||
return false;
|
||||
#else
|
||||
ec = std::make_error_code(std::errc::not_supported);
|
||||
#endif
|
||||
|
@ -128,6 +128,9 @@ test03()
|
||||
fs::copy(from, to);
|
||||
VERIFY( fs::exists(to) );
|
||||
VERIFY( fs::file_size(to) == fs::file_size(from) );
|
||||
|
||||
remove(from);
|
||||
remove(to);
|
||||
}
|
||||
|
||||
// Test is_directory(f) case.
|
||||
@ -138,6 +141,37 @@ test04()
|
||||
auto to = __gnu_test::nonexistent_path();
|
||||
std::error_code ec;
|
||||
|
||||
create_directories(from/"a/b/c");
|
||||
|
||||
{
|
||||
__gnu_test::scoped_file f(to);
|
||||
copy(from, to, ec);
|
||||
VERIFY( ec );
|
||||
}
|
||||
|
||||
__gnu_test::scoped_file f1(from/"a/f1");
|
||||
std::ofstream{f1.path} << "file one";
|
||||
__gnu_test::scoped_file f2(from/"a/b/f2");
|
||||
std::ofstream{f2.path} << "file two";
|
||||
|
||||
copy(from, to, ec);
|
||||
VERIFY( !ec );
|
||||
VERIFY( exists(to) && is_empty(to) );
|
||||
remove(to);
|
||||
|
||||
copy(from, to, fs::copy_options::recursive, ec);
|
||||
VERIFY( !ec );
|
||||
VERIFY( exists(to) && !is_empty(to) );
|
||||
VERIFY( is_regular_file(to/"a/f1") && !is_empty(to/"a/f1") );
|
||||
VERIFY( file_size(from/"a/f1") == file_size(to/"a/f1") );
|
||||
VERIFY( is_regular_file(to/"a/b/f2") && !is_empty(to/"a/b/f2") );
|
||||
VERIFY( file_size(from/"a/b/f2") == file_size(to/"a/b/f2") );
|
||||
VERIFY( is_directory(to/"a/b/c") && is_empty(to/"a/b/c") );
|
||||
|
||||
f1.path.clear();
|
||||
f2.path.clear();
|
||||
remove_all(from, ec);
|
||||
remove_all(to, ec);
|
||||
}
|
||||
|
||||
// Test no-op cases.
|
||||
|
@ -73,6 +73,9 @@ test01()
|
||||
VERIFY( !ec );
|
||||
VERIFY( exists(to) );
|
||||
VERIFY( file_size(to) == file_size(from) );
|
||||
|
||||
remove(from);
|
||||
remove(to);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -0,0 +1,74 @@
|
||||
// Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
//
|
||||
// This file is part of the GNU ISO C++ Library. This library is free
|
||||
// software; you can redistribute it and/or modify it under the
|
||||
// terms of the GNU General Public License as published by the
|
||||
// Free Software Foundation; either version 3, or (at your option)
|
||||
// any later version.
|
||||
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this library; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
// { dg-options "-lstdc++fs" }
|
||||
// { dg-do run { target c++11 } }
|
||||
// { dg-require-filesystem-ts "" }
|
||||
|
||||
#include <experimental/filesystem>
|
||||
#include <testsuite_fs.h>
|
||||
#include <testsuite_hooks.h>
|
||||
|
||||
namespace fs = std::experimental::filesystem;
|
||||
|
||||
|
||||
void
|
||||
test01()
|
||||
{
|
||||
auto p1 = __gnu_test::nonexistent_path();
|
||||
auto p2 = __gnu_test::nonexistent_path();
|
||||
std::error_code ec;
|
||||
bool result;
|
||||
|
||||
result = equivalent(p1, p2, ec);
|
||||
VERIFY( ec );
|
||||
VERIFY( !result );
|
||||
const auto bad_ec = ec;
|
||||
|
||||
__gnu_test::scoped_file f1(p1);
|
||||
result = equivalent(p1, p2, ec);
|
||||
VERIFY( !ec );
|
||||
VERIFY( !result );
|
||||
|
||||
__gnu_test::scoped_file f2(p2);
|
||||
ec = bad_ec;
|
||||
result = equivalent(p1, p2, ec);
|
||||
VERIFY( !ec );
|
||||
VERIFY( !result );
|
||||
|
||||
auto p3 = __gnu_test::nonexistent_path();
|
||||
create_hard_link(p1, p3, ec);
|
||||
if (ec)
|
||||
return; // hard links not supported
|
||||
__gnu_test::scoped_file f3(p3, __gnu_test::scoped_file::adopt_file);
|
||||
|
||||
ec = bad_ec;
|
||||
result = equivalent(p1, p3, ec);
|
||||
VERIFY( !ec );
|
||||
VERIFY( result );
|
||||
|
||||
ec = bad_ec;
|
||||
result = equivalent(p2, p3, ec);
|
||||
VERIFY( !ec );
|
||||
VERIFY( !result );
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
test01();
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
// Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
//
|
||||
// This file is part of the GNU ISO C++ Library. This library is free
|
||||
// software; you can redistribute it and/or modify it under the
|
||||
// terms of the GNU General Public License as published by the
|
||||
// Free Software Foundation; either version 3, or (at your option)
|
||||
// any later version.
|
||||
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this library; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
// { dg-options "-lstdc++fs" }
|
||||
// { dg-do run { target c++11 } }E
|
||||
// { dg-require-filesystem-ts "" }
|
||||
|
||||
#include <experimental/filesystem>
|
||||
#include <testsuite_hooks.h>
|
||||
#include <testsuite_fs.h>
|
||||
|
||||
namespace fs = std::experimental::filesystem;
|
||||
|
||||
void
|
||||
test01()
|
||||
{
|
||||
auto p = __gnu_test::nonexistent_path();
|
||||
create_directory(p);
|
||||
permissions(p, fs::perms::none);
|
||||
std::error_code ec, ec2;
|
||||
|
||||
bool result = fs::is_empty(p, ec);
|
||||
VERIFY( ec == std::make_error_code(std::errc::permission_denied) );
|
||||
VERIFY( !result );
|
||||
|
||||
try {
|
||||
fs::is_empty(p);
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
ec2 = e.code();
|
||||
}
|
||||
VERIFY( ec2 == ec );
|
||||
|
||||
result = fs::is_empty(p/"f", ec);
|
||||
VERIFY( ec == std::make_error_code(std::errc::permission_denied) );
|
||||
VERIFY( !result );
|
||||
|
||||
try {
|
||||
fs::is_empty(p/"f");
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
ec2 = e.code();
|
||||
}
|
||||
VERIFY( ec2 == ec );
|
||||
|
||||
permissions(p, fs::perms::owner_all, ec);
|
||||
remove_all(p, ec);
|
||||
}
|
||||
|
||||
void
|
||||
test02()
|
||||
{
|
||||
auto p = __gnu_test::nonexistent_path();
|
||||
create_directory(p);
|
||||
std::error_code ec, bad_ec = make_error_code(std::errc::invalid_argument);
|
||||
bool empty;
|
||||
|
||||
ec = bad_ec;
|
||||
empty = is_empty(p, ec);
|
||||
VERIFY( !ec );
|
||||
VERIFY( empty );
|
||||
empty = is_empty(p);
|
||||
VERIFY( empty );
|
||||
|
||||
__gnu_test::scoped_file f(p/"f");
|
||||
ec = bad_ec;
|
||||
empty = is_empty(f.path, ec);
|
||||
VERIFY( !ec );
|
||||
VERIFY( empty );
|
||||
empty = is_empty(f.path);
|
||||
VERIFY( empty );
|
||||
|
||||
std::ofstream{f.path.native()} << "data";
|
||||
ec = bad_ec;
|
||||
empty = is_empty(p, ec);
|
||||
VERIFY( !ec );
|
||||
VERIFY( !empty );
|
||||
empty = is_empty(p);
|
||||
VERIFY( !empty );
|
||||
|
||||
ec = bad_ec;
|
||||
empty = is_empty(p, ec);
|
||||
VERIFY( !ec );
|
||||
VERIFY( !empty );
|
||||
empty = is_empty(p);
|
||||
VERIFY( !empty );
|
||||
|
||||
f.path.clear();
|
||||
remove_all(p, ec);
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
test01();
|
||||
test02();
|
||||
}
|
@ -40,6 +40,8 @@ test01()
|
||||
auto result = read_symlink(p, ec);
|
||||
VERIFY( !ec );
|
||||
VERIFY( result == tgt );
|
||||
|
||||
remove(p);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -0,0 +1,92 @@
|
||||
// Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
//
|
||||
// This file is part of the GNU ISO C++ Library. This library is free
|
||||
// software; you can redistribute it and/or modify it under the
|
||||
// terms of the GNU General Public License as published by the
|
||||
// Free Software Foundation; either version 3, or (at your option)
|
||||
// any later version.
|
||||
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this library; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
// { dg-options "-lstdc++fs" }
|
||||
// { dg-do run { target c++11 } }
|
||||
// { dg-require-filesystem-ts "" }
|
||||
|
||||
#include <experimental/filesystem>
|
||||
#include <testsuite_hooks.h>
|
||||
#include <testsuite_fs.h>
|
||||
|
||||
namespace fs = std::experimental::filesystem;
|
||||
|
||||
void
|
||||
test01()
|
||||
{
|
||||
std::error_code ec;
|
||||
std::uintmax_t n;
|
||||
|
||||
n = fs::remove_all("", ec);
|
||||
VERIFY( ec );
|
||||
VERIFY( n == std::uintmax_t(-1) );
|
||||
|
||||
auto p = __gnu_test::nonexistent_path();
|
||||
ec.clear();
|
||||
n = remove_all(p, ec);
|
||||
VERIFY( ec );
|
||||
VERIFY( n == std::uintmax_t(-1) );
|
||||
|
||||
const auto bad_ec = ec;
|
||||
auto link = __gnu_test::nonexistent_path();
|
||||
create_symlink(p, link); // dangling symlink
|
||||
ec = bad_ec;
|
||||
n = remove_all(link, ec);
|
||||
VERIFY( !ec );
|
||||
VERIFY( n == 1 );
|
||||
VERIFY( !exists(symlink_status(link)) ); // DR 2721
|
||||
|
||||
__gnu_test::scoped_file f(p);
|
||||
create_symlink(p, link);
|
||||
ec = bad_ec;
|
||||
n = remove_all(link, ec);
|
||||
VERIFY( !ec );
|
||||
VERIFY( n == 1 );
|
||||
VERIFY( !exists(symlink_status(link)) ); // The symlink is removed, but
|
||||
VERIFY( exists(p) ); // its target is not.
|
||||
|
||||
auto dir = __gnu_test::nonexistent_path();
|
||||
create_directories(dir/"a/b/c");
|
||||
ec = bad_ec;
|
||||
n = remove_all(dir/"a", ec);
|
||||
VERIFY( !ec );
|
||||
VERIFY( n == 3 );
|
||||
VERIFY( exists(dir) );
|
||||
VERIFY( !exists(dir/"a") );
|
||||
|
||||
create_directories(dir/"a/b/c");
|
||||
__gnu_test::scoped_file a1(dir/"a/1");
|
||||
__gnu_test::scoped_file a2(dir/"a/2");
|
||||
__gnu_test::scoped_file b1(dir/"a/b/1");
|
||||
__gnu_test::scoped_file b2(dir/"a/b/2");
|
||||
ec = bad_ec;
|
||||
n = remove_all(dir, ec);
|
||||
VERIFY( !ec );
|
||||
VERIFY( n == 8 );
|
||||
VERIFY( !exists(dir) );
|
||||
|
||||
a1.path.clear();
|
||||
a2.path.clear();
|
||||
b1.path.clear();
|
||||
b2.path.clear();
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
test01();
|
||||
}
|
@ -107,7 +107,7 @@ namespace __gnu_test
|
||||
|
||||
scoped_file(path_type p, adopt_file_t) : path(p) { }
|
||||
|
||||
~scoped_file() { remove(path); }
|
||||
~scoped_file() { if (!path.empty()) remove(path); }
|
||||
|
||||
path_type path;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user