PR libstdc++/83626 Don't report errors when removing non-existent files

Backport from mainline
2018-01-05  Jonathan Wakely  <jwakely@redhat.com>

	PR libstdc++/83626
	* src/filesystem/ops.cc (remove(const path&, error_code&)): Do not
	report an error for ENOENT.
	(remove_all(const path&)): Fix type of result variable.
	(remove_all(const path&, error_code&)): Use non-throwing increment
	for directory iterator. Call POSIX remove directly to avoid redundant
	calls to symlink_status. Do not report errors for ENOENT.
	* testsuite/experimental/filesystem/operations/remove_all.cc: Test
        throwing overload.

Backport from mainline
2018-01-04  Jonathan Wakely  <jwakely@redhat.com>

	PR libstdc++/83626
	* src/filesystem/ops.cc (remove(const path&, error_code&))): Remove
	redundant call to ec.clear().
	(remove_all(const path&, error_code&))): Do not return an error for
	non-existent paths.
	* testsuite/experimental/filesystem/operations/remove.cc: New test.
	* testsuite/experimental/filesystem/operations/remove_all.cc: Fix
	expected results for non-existent paths.

From-SVN: r256287
This commit is contained in:
Jonathan Wakely 2018-01-05 21:27:25 +00:00 committed by Jonathan Wakely
parent df7dd7127c
commit 72eaf75b30
4 changed files with 196 additions and 25 deletions

View File

@ -1,5 +1,30 @@
2018-01-05 Jonathan Wakely <jwakely@redhat.com>
Backport from mainline
2018-01-05 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/83626
* src/filesystem/ops.cc (remove(const path&, error_code&)): Do not
report an error for ENOENT.
(remove_all(const path&)): Fix type of result variable.
(remove_all(const path&, error_code&)): Use non-throwing increment
for directory iterator. Call POSIX remove directly to avoid redundant
calls to symlink_status. Do not report errors for ENOENT.
* testsuite/experimental/filesystem/operations/remove_all.cc: Test
throwing overload.
Backport from mainline
2018-01-04 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/83626
* src/filesystem/ops.cc (remove(const path&, error_code&))): Remove
redundant call to ec.clear().
(remove_all(const path&, error_code&))): Do not return an error for
non-existent paths.
* testsuite/experimental/filesystem/operations/remove.cc: New test.
* testsuite/experimental/filesystem/operations/remove_all.cc: Fix
expected results for non-existent paths.
Backport from mainline
2017-10-25 Jonathan Wakely <jwakely@redhat.com>

View File

@ -1246,7 +1246,7 @@ fs::remove(const path& p)
{
error_code ec;
bool result = fs::remove(p, ec);
if (ec.value())
if (ec)
_GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
return result;
}
@ -1254,16 +1254,23 @@ fs::remove(const path& p)
bool
fs::remove(const path& p, error_code& ec) noexcept
{
if (exists(symlink_status(p, ec)))
const auto s = symlink_status(p, ec);
if (!status_known(s))
return false;
if (s.type() == file_type::not_found)
{
if (::remove(p.c_str()) == 0)
{
ec.clear();
return true;
}
else
ec.assign(errno, std::generic_category());
ec.clear();
return false; // Nothing to do, not an error.
}
if (::remove(p.c_str()) == 0)
{
ec.clear();
return true;
}
else if (errno == ENOENT)
ec.clear();
else
ec.assign(errno, std::generic_category());
return false;
}
@ -1272,8 +1279,8 @@ std::uintmax_t
fs::remove_all(const path& p)
{
error_code ec;
bool result = remove_all(p, ec);
if (ec.value())
const auto result = remove_all(p, ec);
if (ec)
_GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
return result;
}
@ -1281,14 +1288,33 @@ fs::remove_all(const path& p)
std::uintmax_t
fs::remove_all(const path& p, error_code& ec) noexcept
{
auto fs = symlink_status(p, ec);
uintmax_t count = 0;
if (ec.value() == 0 && fs.type() == file_type::directory)
for (directory_iterator d(p, ec), end; ec.value() == 0 && d != end; ++d)
count += fs::remove_all(d->path(), ec);
if (ec.value())
const auto s = symlink_status(p, ec);
if (!status_known(s))
return -1;
return fs::remove(p, ec) ? ++count : -1; // fs:remove() calls ec.clear()
ec.clear();
if (s.type() == file_type::not_found)
return 0;
uintmax_t count = 0;
if (s.type() == file_type::directory)
{
for (directory_iterator d(p, ec), end; !ec && d != end; d.increment(ec))
count += fs::remove_all(d->path(), ec);
if (ec.value() == ENOENT)
ec.clear();
else if (ec)
return -1;
}
if (::remove(p.c_str()) == 0)
++count;
else if (errno != ENOENT)
{
ec.assign(errno, std::generic_category());
return -1;
}
return count;
}
void

View File

@ -0,0 +1,100 @@
// Copyright (C) 2018 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;
const std::error_code bad_ec = make_error_code(std::errc::invalid_argument);
bool n;
n = fs::remove("", ec);
VERIFY( !ec ); // This seems odd, but is what the standard requires.
VERIFY( !n );
auto p = __gnu_test::nonexistent_path();
ec = bad_ec;
n = remove(p, ec);
VERIFY( !ec );
VERIFY( !n );
auto link = __gnu_test::nonexistent_path();
create_symlink(p, link); // dangling symlink
ec = bad_ec;
n = remove(link, ec);
VERIFY( !ec );
VERIFY( n );
VERIFY( !exists(symlink_status(link)) );
__gnu_test::scoped_file f(p);
create_symlink(p, link);
ec = bad_ec;
n = remove(link, ec);
VERIFY( !ec );
VERIFY( n );
VERIFY( !exists(symlink_status(link)) ); // The symlink is removed, but
VERIFY( exists(p) ); // its target is not.
ec = bad_ec;
n = remove(p, ec);
VERIFY( !ec );
VERIFY( n );
VERIFY( !exists(symlink_status(p)) );
const auto dir = __gnu_test::nonexistent_path();
create_directories(dir/"a/b");
ec.clear();
n = remove(dir/"a", ec);
VERIFY( ec );
VERIFY( !n );
VERIFY( exists(dir/"a/b") );
permissions(dir, fs::perms::none, ec);
if (!ec)
{
ec.clear();
n = remove(dir/"a/b", ec);
VERIFY( ec );
VERIFY( !n );
permissions(dir, fs::perms::owner_all, ec);
}
ec = bad_ec;
n = remove(dir/"a/b", ec);
VERIFY( !ec );
VERIFY( n );
VERIFY( !exists(dir/"a/b") );
remove(dir/"a", ec);
remove(dir, ec);
}
int
main()
{
test01();
}

View File

@ -29,19 +29,19 @@ void
test01()
{
std::error_code ec;
const std::error_code bad_ec = make_error_code(std::errc::invalid_argument);
std::uintmax_t n;
n = fs::remove_all("", ec);
VERIFY( ec );
VERIFY( n == std::uintmax_t(-1) );
VERIFY( !ec ); // This seems odd, but is what the TS requires.
VERIFY( n == 0 );
auto p = __gnu_test::nonexistent_path();
ec.clear();
ec = bad_ec;
n = remove_all(p, ec);
VERIFY( ec );
VERIFY( n == std::uintmax_t(-1) );
VERIFY( !ec );
VERIFY( n == 0 );
const auto bad_ec = ec;
auto link = __gnu_test::nonexistent_path();
create_symlink(p, link); // dangling symlink
ec = bad_ec;
@ -59,7 +59,7 @@ test01()
VERIFY( !exists(symlink_status(link)) ); // The symlink is removed, but
VERIFY( exists(p) ); // its target is not.
auto dir = __gnu_test::nonexistent_path();
const auto dir = __gnu_test::nonexistent_path();
create_directories(dir/"a/b/c");
ec = bad_ec;
n = remove_all(dir/"a", ec);
@ -85,8 +85,28 @@ test01()
b2.path.clear();
}
void
test02()
{
const auto dir = __gnu_test::nonexistent_path();
create_directories(dir/"a/b/c");
std::uintmax_t n = remove_all(dir/"a");
VERIFY( n == 3 );
VERIFY( exists(dir) );
VERIFY( !exists(dir/"a") );
n = remove_all(dir/"a");
VERIFY( n == 0 );
VERIFY( exists(dir) );
n = remove_all(dir);
VERIFY( n == 1 );
VERIFY( !exists(dir) );
}
int
main()
{
test01();
test02();
}