aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2020-01-08 21:51:38 +0000
committerJonathan Wakely <redi@gcc.gnu.org>2020-01-08 21:51:38 +0000
commitec79804a746c0ee40a8812ae6709e66a03266509 (patch)
treeb59933d0f609b119a54d23c0eedc490b8523ed68 /libstdc++-v3
parent2c6a8b0a156849909b5022a754b24bcda33900ce (diff)
downloadgcc-ec79804a746c0ee40a8812ae6709e66a03266509.zip
gcc-ec79804a746c0ee40a8812ae6709e66a03266509.tar.gz
gcc-ec79804a746c0ee40a8812ae6709e66a03266509.tar.bz2
libstdc++: Fix error handling in filesystem::remove_all (PR93201)
When recursing into a directory, any errors that occur while removing a directory entry are ignored, because the subsequent increment of the directory iterator clears the error_code object. This fixes that bug by checking the result of each recursive operation before incrementing. This is a change in observable behaviour, because previously other directory entries would still be removed even if one (or more) couldn't be removed due to errors. Now the operation stops on the first error, which is what the code intended to do all along. The standard doesn't specify what happens in this case (because the order that the entries are processed is unspecified anyway). PR libstdc++/93201 * src/c++17/fs_ops.cc (remove_all(const path&, error_code&)): Check result of recursive call before incrementing iterator. * src/filesystem/ops.cc (remove_all(const path&, error_code&)): Likewise. * testsuite/27_io/filesystem/operations/remove_all.cc: Check errors are reported correctly. * testsuite/experimental/filesystem/operations/remove_all.cc: Likewise. From-SVN: r280021
Diffstat (limited to 'libstdc++-v3')
-rw-r--r--libstdc++-v3/ChangeLog11
-rw-r--r--libstdc++-v3/src/filesystem/ops.cc17
-rw-r--r--libstdc++-v3/src/filesystem/std-ops.cc17
-rw-r--r--libstdc++-v3/testsuite/27_io/filesystem/operations/remove_all.cc33
-rw-r--r--libstdc++-v3/testsuite/experimental/filesystem/operations/remove_all.cc33
5 files changed, 99 insertions, 12 deletions
diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog
index 16f3f10..cba45e0 100644
--- a/libstdc++-v3/ChangeLog
+++ b/libstdc++-v3/ChangeLog
@@ -1,3 +1,14 @@
+2020-01-08 Jonathan Wakely <jwakely@redhat.com>
+
+ PR libstdc++/93201
+ * src/c++17/fs_ops.cc (remove_all(const path&, error_code&)): Check
+ result of recursive call before incrementing iterator.
+ * src/filesystem/ops.cc (remove_all(const path&, error_code&)):
+ Likewise.
+ * testsuite/27_io/filesystem/operations/remove_all.cc: Check errors
+ are reported correctly.
+ * testsuite/experimental/filesystem/operations/remove_all.cc: Likewise.
+
2019-10-24 Jonathan Wakely <jwakely@redhat.com>
Backport from mainline
diff --git a/libstdc++-v3/src/filesystem/ops.cc b/libstdc++-v3/src/filesystem/ops.cc
index de29062..4b0a4ff 100644
--- a/libstdc++-v3/src/filesystem/ops.cc
+++ b/libstdc++-v3/src/filesystem/ops.cc
@@ -1070,12 +1070,17 @@ fs::remove_all(const path& p, error_code& ec) noexcept
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;
+ directory_iterator d(p, ec), end;
+ while (!ec && d != end)
+ {
+ const auto removed = fs::remove_all(d->path(), ec);
+ if (removed == numeric_limits<uintmax_t>::max())
+ return -1;
+ count += removed;
+ d.increment(ec);
+ if (ec)
+ return -1;
+ }
}
if (fs::remove(p, ec))
diff --git a/libstdc++-v3/src/filesystem/std-ops.cc b/libstdc++-v3/src/filesystem/std-ops.cc
index 40de0de..97fcfa1 100644
--- a/libstdc++-v3/src/filesystem/std-ops.cc
+++ b/libstdc++-v3/src/filesystem/std-ops.cc
@@ -1350,12 +1350,17 @@ fs::remove_all(const path& p, error_code& ec)
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;
+ directory_iterator d(p, ec), end;
+ while (!ec && d != end)
+ {
+ const auto removed = fs::remove_all(d->path(), ec);
+ if (removed == numeric_limits<uintmax_t>::max())
+ return -1;
+ count += removed;
+ d.increment(ec);
+ if (ec)
+ return -1;
+ }
}
if (fs::remove(p, ec))
diff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/remove_all.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/remove_all.cc
index 633cde5..a3904ea 100644
--- a/libstdc++-v3/testsuite/27_io/filesystem/operations/remove_all.cc
+++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/remove_all.cc
@@ -104,9 +104,42 @@ test02()
VERIFY( !exists(dir) );
}
+void
+test04()
+{
+#if defined(__MINGW32__) || defined(__MINGW64__)
+ // no permissions
+#else
+ // PR libstdc++/93201
+ std::error_code ec;
+ std::uintmax_t n;
+
+ auto dir = __gnu_test::nonexistent_path();
+ fs::create_directory(dir);
+ __gnu_test::scoped_file f(dir/"file");
+ // remove write permission on the directory:
+ fs::permissions(dir, fs::perms::owner_read|fs::perms::owner_exec);
+ n = fs::remove_all(dir, ec);
+ VERIFY( n == -1 );
+ VERIFY( ec == std::errc::permission_denied ); // not ENOTEMPTY
+
+ try {
+ fs::remove_all(dir);
+ VERIFY( false );
+ } catch (const fs::filesystem_error& e) {
+ VERIFY( e.code() == std::errc::permission_denied );
+ // First path is the argument to remove_all
+ VERIFY( e.path1() == dir );
+ }
+
+ fs::permissions(dir, fs::perms::owner_write, fs::perm_options::add);
+#endif
+}
+
int
main()
{
test01();
test02();
+ test04();
}
diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/remove_all.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/remove_all.cc
index ddf150b..af78b9e 100644
--- a/libstdc++-v3/testsuite/experimental/filesystem/operations/remove_all.cc
+++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/remove_all.cc
@@ -104,9 +104,42 @@ test02()
VERIFY( !exists(dir) );
}
+void
+test04()
+{
+#if defined(__MINGW32__) || defined(__MINGW64__)
+ // no permissions
+#else
+ // PR libstdc++/93201
+ std::error_code ec;
+ std::uintmax_t n;
+
+ auto dir = __gnu_test::nonexistent_path();
+ fs::create_directory(dir);
+ __gnu_test::scoped_file f(dir/"file");
+ // remove write permission on the directory:
+ fs::permissions(dir, fs::perms::owner_read|fs::perms::owner_exec);
+ n = fs::remove_all(dir, ec);
+ VERIFY( n == -1 );
+ VERIFY( ec == std::errc::permission_denied ); // not ENOTEMPTY
+
+ try {
+ fs::remove_all(dir);
+ VERIFY( false );
+ } catch (const fs::filesystem_error& e) {
+ VERIFY( e.code() == std::errc::permission_denied );
+ // First path is the argument to remove_all
+ VERIFY( e.path1() == dir );
+ }
+
+ fs::permissions(dir, fs::perms::owner_write|fs::perms::add_perms);
+#endif
+}
+
int
main()
{
test01();
test02();
+ test04();
}