blob: 1b664bc53458b8468fcb74d507de32e5d6bc4052 [file] [log] [blame]
// 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();
}