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:
Jonathan Wakely 2016-10-25 16:32:37 +01:00 committed by Jonathan Wakely
parent 1cf1719bc0
commit ec04aad76d
9 changed files with 368 additions and 13 deletions

View File

@ -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> 2016-10-24 Jonathan Wakely <jwakely@redhat.com>
* src/filesystem/ops.cc (is_empty): Fix error handling. * src/filesystem/ops.cc (is_empty): Fix error handling.

View File

@ -350,6 +350,8 @@ namespace
from_st = &st2; from_st = &st2;
} }
f = make_file_status(*from_st); 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)) if (!is_regular_file(f))
{ {
ec = std::make_error_code(std::errc::not_supported); ec = std::make_error_code(std::errc::not_supported);
@ -360,8 +362,13 @@ namespace
if (exists(t)) if (exists(t))
{ {
if (!is_other(t) && !is_other(f) if (!is_regular_file(t))
&& to_st->st_dev == from_st->st_dev {
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) && to_st->st_ino == from_st->st_ino)
{ {
ec = std::make_error_code(std::errc::file_exists); ec = std::make_error_code(std::errc::file_exists);
@ -912,7 +919,7 @@ fs::equivalent(const path& p1, const path& p2)
{ {
error_code ec; error_code ec;
auto result = equivalent(p1, p2, ec); auto result = equivalent(p1, p2, ec);
if (ec.value()) if (ec)
_GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence", _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
p1, p2, ec)); p1, p2, ec));
return result; return result;
@ -922,25 +929,42 @@ bool
fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
{ {
#ifdef _GLIBCXX_HAVE_SYS_STAT_H #ifdef _GLIBCXX_HAVE_SYS_STAT_H
int err = 0;
file_status s1, s2;
stat_type st1, st2; 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)) if (is_other(s1) && is_other(s2))
{ {
ec = std::make_error_code(std::errc::not_supported); ec = std::make_error_code(std::errc::not_supported);
return false; return false;
} }
ec.clear(); ec.clear();
if (is_other(s1) || is_other(s2))
return false;
return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino; return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
} }
else if (is_not_found_errno(errno)) else if (!exists(s1) && !exists(s2))
{ ec = std::make_error_code(std::errc::no_such_file_or_directory);
ec = std::make_error_code(std::errc::no_such_file_or_directory); else if (err)
return false; ec.assign(err, std::generic_category());
} else
ec.assign(errno, std::generic_category()); ec.clear();
return false;
#else #else
ec = std::make_error_code(std::errc::not_supported); ec = std::make_error_code(std::errc::not_supported);
#endif #endif

View File

@ -128,6 +128,9 @@ test03()
fs::copy(from, to); fs::copy(from, to);
VERIFY( fs::exists(to) ); VERIFY( fs::exists(to) );
VERIFY( fs::file_size(to) == fs::file_size(from) ); VERIFY( fs::file_size(to) == fs::file_size(from) );
remove(from);
remove(to);
} }
// Test is_directory(f) case. // Test is_directory(f) case.
@ -138,6 +141,37 @@ test04()
auto to = __gnu_test::nonexistent_path(); auto to = __gnu_test::nonexistent_path();
std::error_code ec; 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. // Test no-op cases.

View File

@ -73,6 +73,9 @@ test01()
VERIFY( !ec ); VERIFY( !ec );
VERIFY( exists(to) ); VERIFY( exists(to) );
VERIFY( file_size(to) == file_size(from) ); VERIFY( file_size(to) == file_size(from) );
remove(from);
remove(to);
} }
int int

View File

@ -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();
}

View File

@ -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();
}

View File

@ -40,6 +40,8 @@ test01()
auto result = read_symlink(p, ec); auto result = read_symlink(p, ec);
VERIFY( !ec ); VERIFY( !ec );
VERIFY( result == tgt ); VERIFY( result == tgt );
remove(p);
} }
int int

View File

@ -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();
}

View File

@ -107,7 +107,7 @@ namespace __gnu_test
scoped_file(path_type p, adopt_file_t) : path(p) { } scoped_file(path_type p, adopt_file_t) : path(p) { }
~scoped_file() { remove(path); } ~scoped_file() { if (!path.empty()) remove(path); }
path_type path; path_type path;
}; };