| // Copyright (C) 2020-2023 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-do run { target { c++17 } } } |
| // { dg-require-filesystem-ts "" } |
| |
| #include <filesystem> |
| #include <cerrno> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <dirent.h> |
| #include <testsuite_hooks.h> |
| #include <testsuite_fs.h> |
| |
| int choice; |
| |
| extern "C" struct dirent* readdir(DIR*) |
| { |
| // On some targets dirent::d_name is very small, but the OS allocates |
| // a trailing char array after the dirent struct. Emulate that here. |
| union State |
| { |
| struct dirent d; |
| char buf[sizeof(struct dirent) + 16] = {}; |
| }; |
| |
| static State state; |
| char* d_name = state.buf + offsetof(struct dirent, d_name); |
| |
| switch (choice) |
| { |
| case 1: |
| state.d.d_ino = 999; |
| #if defined _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE && defined DT_REG |
| state.d.d_type = DT_REG; |
| #endif |
| state.d.d_reclen = 0; |
| std::char_traits<char>::copy(d_name, "file", 5); |
| choice = 0; |
| return &state.d; |
| case 2: |
| state.d.d_ino = 111; |
| #if defined _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE && defined DT_DIR |
| state.d.d_type = DT_DIR; |
| #endif |
| state.d.d_reclen = 60; |
| std::char_traits<char>::copy(d_name, "subdir", 7); |
| choice = 1; |
| return &state.d; |
| default: |
| errno = EIO; |
| return nullptr; |
| } |
| return &state.d; |
| } |
| |
| 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; |
| const 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 |
| |
| // Cannot use fs::remove_all here because that uses |
| // recursive_directory_iterator which would use the fake readdir above. |
| #ifndef _GLIBCXX_FILESYSTEM_IS_WINDOWS |
| ::rmdir((dir/"subdir").c_str()); |
| ::rmdir(dir.c_str()); |
| #endif |
| } |
| |
| int |
| main() |
| { |
| test01(); |
| test02(); |
| } |