From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 1926A385B80B for ; Tue, 1 Feb 2022 21:59:09 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 1926A385B80B Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-625-TaLj2lR0O62Q9nd0jg51NQ-1; Tue, 01 Feb 2022 16:59:05 -0500 X-MC-Unique: TaLj2lR0O62Q9nd0jg51NQ-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 7AEE285EE60; Tue, 1 Feb 2022 21:59:04 +0000 (UTC) Received: from localhost (unknown [10.33.37.88]) by smtp.corp.redhat.com (Postfix) with ESMTP id 156CDE721; Tue, 1 Feb 2022 21:59:03 +0000 (UTC) From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Reset filesystem::recursive_directory_iterator on error Date: Tue, 1 Feb 2022 21:59:03 +0000 Message-Id: <20220201215903.2191074-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII" X-Spam-Status: No, score=-13.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: libstdc++@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++ mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 01 Feb 2022 21:59:11 -0000 Tested powerpc64le-linux, pushed to trunk. 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 +- .../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(); +} -- 2.34.1