From ec09a5335f0ade7071f6157dfd97dbb3de3e4f97 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Mon, 31 Jan 2022 21:12:53 +0000 Subject: libstdc++: Reset filesystem::recursive_directory_iterator on error The standard requires directory iterators to become equal to the end iterator value if they report an error. Some members functions of filesystem::recursive_directory_iterator fail to do that. libstdc++-v3/ChangeLog: * src/c++17/fs_dir.cc (recursive_directory_iterator::increment): Reset state to past-the-end iterator on error. (fs::recursive_directory_iterator::pop(error_code&)): Likewise. (fs::recursive_directory_iterator::pop()): Check _M_dirs before it might get reset. * src/filesystem/dir.cc (recursive_directory_iterator): Likewise, for the TS implementation. * testsuite/27_io/filesystem/iterators/error_reporting.cc: New test. * testsuite/experimental/filesystem/iterators/error_reporting.cc: New test. --- libstdc++-v3/src/c++17/fs_dir.cc | 12 +- libstdc++-v3/src/filesystem/dir.cc | 12 +- .../27_io/filesystem/iterators/error_reporting.cc | 135 ++++++++++++++++++++ .../filesystem/iterators/error_reporting.cc | 136 +++++++++++++++++++++ 4 files changed, 291 insertions(+), 4 deletions(-) create mode 100644 libstdc++-v3/testsuite/27_io/filesystem/iterators/error_reporting.cc create mode 100644 libstdc++-v3/testsuite/experimental/filesystem/iterators/error_reporting.cc diff --git a/libstdc++-v3/src/c++17/fs_dir.cc b/libstdc++-v3/src/c++17/fs_dir.cc index e050304c19a..149a8b0740c 100644 --- a/libstdc++-v3/src/c++17/fs_dir.cc +++ b/libstdc++-v3/src/c++17/fs_dir.cc @@ -311,6 +311,10 @@ fs::recursive_directory_iterator::increment(error_code& ec) return *this; } } + + if (ec) + _M_dirs.reset(); + return *this; } @@ -334,16 +338,20 @@ fs::recursive_directory_iterator::pop(error_code& ec) ec.clear(); return; } - } while (!_M_dirs->top().advance(skip_permission_denied, ec)); + } while (!_M_dirs->top().advance(skip_permission_denied, ec) && !ec); + + if (ec) + _M_dirs.reset(); } void fs::recursive_directory_iterator::pop() { + const bool dereferenceable = _M_dirs != nullptr; error_code ec; pop(ec); if (ec) - _GLIBCXX_THROW_OR_ABORT(filesystem_error(_M_dirs + _GLIBCXX_THROW_OR_ABORT(filesystem_error(dereferenceable ? "recursive directory iterator cannot pop" : "non-dereferenceable recursive directory iterator cannot pop", ec)); diff --git a/libstdc++-v3/src/filesystem/dir.cc b/libstdc++-v3/src/filesystem/dir.cc index d5b11f25297..ac9e70da516 100644 --- a/libstdc++-v3/src/filesystem/dir.cc +++ b/libstdc++-v3/src/filesystem/dir.cc @@ -298,6 +298,10 @@ fs::recursive_directory_iterator::increment(error_code& ec) noexcept return *this; } } + + if (ec) + _M_dirs.reset(); + return *this; } @@ -321,16 +325,20 @@ fs::recursive_directory_iterator::pop(error_code& ec) ec.clear(); return; } - } while (!_M_dirs->top().advance(skip_permission_denied, ec)); + } while (!_M_dirs->top().advance(skip_permission_denied, ec) && !ec); + + if (ec) + _M_dirs.reset(); } void fs::recursive_directory_iterator::pop() { + const bool dereferenceable = _M_dirs != nullptr; error_code ec; pop(ec); if (ec) - _GLIBCXX_THROW_OR_ABORT(filesystem_error(_M_dirs + _GLIBCXX_THROW_OR_ABORT(filesystem_error(dereferenceable ? "recursive directory iterator cannot pop" : "non-dereferenceable recursive directory iterator cannot pop", ec)); diff --git a/libstdc++-v3/testsuite/27_io/filesystem/iterators/error_reporting.cc b/libstdc++-v3/testsuite/27_io/filesystem/iterators/error_reporting.cc new file mode 100644 index 00000000000..81ef1069367 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/filesystem/iterators/error_reporting.cc @@ -0,0 +1,135 @@ +// Copyright (C) 2020-2022 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 +// . + +// { dg-do run { target { c++17 } } } +// { dg-require-filesystem-ts "" } + +#include +#include +#include +#include +#include +#include +#include + +int choice; + +struct dirent global_dirent; + +extern "C" struct dirent* readdir(DIR*) +{ + switch (choice) + { + case 1: + global_dirent.d_ino = 999; + global_dirent.d_type = DT_REG; + global_dirent.d_reclen = 0; + std::char_traits::copy(global_dirent.d_name, "file", 5); + choice = 0; + return &global_dirent; + case 2: + global_dirent.d_ino = 111; + global_dirent.d_type = DT_DIR; + global_dirent.d_reclen = 60; + std::char_traits::copy(global_dirent.d_name, "subdir", 7); + choice = 1; + return &global_dirent; + default: + errno = EIO; + return nullptr; + } + return &global_dirent; +} + +void +test01() +{ + namespace fs = std::filesystem; + std::error_code ec; + choice = 1; + fs::recursive_directory_iterator it(".", ec); + if (choice == 0) // custom readdir was called + { + it.increment(ec); + VERIFY( ec.value() == EIO ); + VERIFY( it == end(it) ); + } + else + { + puts("Custom readdir not used, cannot test error handling"); + exit(0); + } + +#if __cpp_exceptions + choice = 1; + fs::recursive_directory_iterator it2(".", ec); + if (choice == 0) + { + try { + ++it2; + VERIFY( false ); + } catch (const fs::filesystem_error& e) { + VERIFY( e.code().value() == EIO ); + VERIFY( it2 == end(it2) ); + } + } +#endif +} + +void +test02() +{ + namespace fs = std::filesystem; + auto dir = __gnu_test::nonexistent_path(); + fs::create_directories(dir/"subdir"); + + std::error_code ec; + choice = 2; + fs::recursive_directory_iterator it(dir, ec); + if (choice == 1) + { + ++it; + it.pop(ec); + VERIFY( ec.value() == EIO ); + VERIFY( it == end(it) ); + } + +#if __cpp_exceptions + choice = 2; + fs::recursive_directory_iterator it2(dir, ec); + if (choice == 1) + { + ++it2; + try { + it2.pop(); + VERIFY( false ); + } catch (const fs::filesystem_error& e) { + VERIFY( e.code().value() == EIO ); + VERIFY( it2 == end(it2) ); + } + } +#endif + + fs::remove_all(dir, ec); +} + +int +main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/experimental/filesystem/iterators/error_reporting.cc b/libstdc++-v3/testsuite/experimental/filesystem/iterators/error_reporting.cc new file mode 100644 index 00000000000..ade62732028 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/iterators/error_reporting.cc @@ -0,0 +1,136 @@ +// Copyright (C) 2020-2022 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 +// . + +// { dg-options "-DUSE_FILESYSTEM_TS -lstdc++fs" } +// { dg-do run { target { c++11 } } } +// { dg-require-filesystem-ts "" } + +#include +#include +#include +#include +#include +#include +#include + +int choice; + +struct dirent global_dirent; + +extern "C" struct dirent* readdir(DIR*) +{ + switch (choice) + { + case 1: + global_dirent.d_ino = 999; + global_dirent.d_type = DT_REG; + global_dirent.d_reclen = 0; + std::char_traits::copy(global_dirent.d_name, "file", 5); + choice = 0; + return &global_dirent; + case 2: + global_dirent.d_ino = 111; + global_dirent.d_type = DT_DIR; + global_dirent.d_reclen = 60; + std::char_traits::copy(global_dirent.d_name, "subdir", 7); + choice = 1; + return &global_dirent; + default: + errno = EIO; + return nullptr; + } + return &global_dirent; +} + +void +test01() +{ + namespace fs = std::experimental::filesystem; + std::error_code ec; + choice = 1; + fs::recursive_directory_iterator it(".", ec); + if (choice == 0) // custom readdir was called + { + it.increment(ec); + VERIFY( ec.value() == EIO ); + VERIFY( it == end(it) ); + } + else + { + puts("Custom readdir not used, cannot test error handling"); + exit(0); + } + +#if __cpp_exceptions + choice = 1; + fs::recursive_directory_iterator it2(".", ec); + if (choice == 0) // custom readdir was called + { + try { + ++it2; + VERIFY( false ); + } catch (const fs::filesystem_error& e) { + VERIFY( e.code().value() == EIO ); + VERIFY( it2 == end(it2) ); + } + } +#endif +} + +void +test02() +{ + namespace fs = std::experimental::filesystem; + auto dir = __gnu_test::nonexistent_path(); + fs::create_directories(dir/"subdir"); + + std::error_code ec; + choice = 2; + fs::recursive_directory_iterator it(dir, ec); + if (choice == 1) + { + ++it; + it.pop(ec); + VERIFY( ec.value() == EIO ); + VERIFY( it == end(it) ); + } + +#if __cpp_exceptions + choice = 2; + fs::recursive_directory_iterator it2(dir, ec); + if (choice == 1) + { + ++it2; + try { + it2.pop(); + VERIFY( false ); + } catch (const fs::filesystem_error& e) { + VERIFY( e.code().value() == EIO ); + VERIFY( it2 == end(it2) ); + } + } +#endif + + fs::remove_all(dir, ec); +} + +int +main() +{ + test01(); + test02(); +} -- cgit v1.2.3