From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2181) id D2EFB3856DDE; Wed, 15 Jun 2022 08:14:49 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D2EFB3856DDE MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jonathan Wakely To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r10-10839] libstdc++: Reset filesystem::recursive_directory_iterator on error X-Act-Checkin: gcc X-Git-Author: Jonathan Wakely X-Git-Refname: refs/heads/releases/gcc-10 X-Git-Oldrev: 884424353e3577b5f17b7dee4f01be4128943604 X-Git-Newrev: 92d5f0c71ea913b9b36c47ab392cc1225434ce3c Message-Id: <20220615081449.D2EFB3856DDE@sourceware.org> Date: Wed, 15 Jun 2022 08:14:49 +0000 (GMT) X-BeenThere: libstdc++-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 15 Jun 2022 08:14:49 -0000 https://gcc.gnu.org/g:92d5f0c71ea913b9b36c47ab392cc1225434ce3c commit r10-10839-g92d5f0c71ea913b9b36c47ab392cc1225434ce3c Author: Jonathan Wakely Date: Mon Jan 31 21:12:53 2022 +0000 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. (cherry picked from commit ec09a5335f0ade7071f6157dfd97dbb3de3e4f97) Diff: --- 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(-) diff --git a/libstdc++-v3/src/c++17/fs_dir.cc b/libstdc++-v3/src/c++17/fs_dir.cc index 21a80b7d56b..d728ac70e8a 100644 --- a/libstdc++-v3/src/c++17/fs_dir.cc +++ b/libstdc++-v3/src/c++17/fs_dir.cc @@ -306,6 +306,10 @@ fs::recursive_directory_iterator::increment(error_code& ec) return *this; } } + + if (ec) + _M_dirs.reset(); + return *this; } @@ -329,16 +333,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() { + [[maybe_unused]] 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 64acb68f7d2..300e8c3ec64 100644 --- a/libstdc++-v3/src/filesystem/dir.cc +++ b/libstdc++-v3/src/filesystem/dir.cc @@ -289,6 +289,10 @@ fs::recursive_directory_iterator::increment(error_code& ec) noexcept return *this; } } + + if (ec) + _M_dirs.reset(); + return *this; } @@ -312,16 +316,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() { + [[maybe_unused]] 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(); +}