{
auto& top = _M_dirs->top();
+#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
+ // _Dir::unlink uses fs::remove which uses std::system_category() for
+ // Windows errror codes, so we can't just check for EPERM and EISDIR.
+ // Use directory_entry::refresh() here to check if we have a directory.
+ // This can be a TOCTTOU race, but we don't have openat or unlinkat to
+ // solve that on Windows, and generally don't support symlinks anyway.
+ if (top.entry._M_type == file_type::none)
+ top.entry.refresh();
+#endif
+
if (top.entry._M_type == file_type::directory)
{
_Dir dir = top.open_subdir(skip_permission_denied, nofollow, ec);
}
else if (top.unlink(ec))
break; // Success
+#if ! _GLIBCXX_FILESYSTEM_IS_WINDOWS
else if (top.entry._M_type == file_type::none)
{
// We did not have a cached type, so it's possible that top.entry
// is actually a directory, and that's why the unlink above failed.
#ifdef EPERM
- // POSIX.1-2017 says unlinking a directory returns EPERM,
+ // POSIX.1-2017 says unlink on a directory returns EPERM,
// but LSB allows EISDIR too. Some targets don't even define EPERM.
if (ec.value() == EPERM || ec.value() == EISDIR)
#else
continue;
}
}
+#endif
}
if (!ec)
std::uintmax_t
fs::remove_all(const path& p)
{
+ error_code ec;
uintmax_t count = 0;
- auto st = filesystem::status(p);
- if (!exists(st))
- return 0;
- if (is_directory(st))
+ recursive_directory_iterator dir(p, directory_options{64|128}, ec);
+ switch (ec.value()) // N.B. assumes ec.category() == std::generic_category()
+ {
+ case 0:
+ // Iterate over the directory removing everything.
{
- recursive_directory_iterator dir(p, directory_options{64|128}), end;
- path failed;
+ const recursive_directory_iterator end;
while (dir != end)
{
- failed = dir->path();
- dir.__erase();
+ dir.__erase(); // throws on error
++count;
}
}
+ // Directory is empty now, will remove it below.
+ break;
+ case ENOENT:
+ // Our work here is done.
+ return 0;
+ case ENOTDIR:
+ case ELOOP:
+ // Not a directory, will remove below.
+ break;
+ default:
+ // An error occurred.
+ _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
+ }
+
+ // Remove p itself, which is either a non-directory or is now empty.
return count + fs::remove(p);
}
{
uintmax_t count = 0;
recursive_directory_iterator dir(p, directory_options{64|128}, ec);
- switch (ec.value())
+ switch (ec.value()) // N.B. assumes ec.category() == std::generic_category()
{
case 0:
+ // Iterate over the directory removing everything.
{
- recursive_directory_iterator end;
+ const recursive_directory_iterator end;
while (dir != end)
{
dir.__erase(&ec);
++count;
}
}
+ // Directory is empty now, will remove it below.
break;
case ENOENT:
// Our work here is done.
// An error occurred.
return -1;
}
+
// Remove p itself, which is either a non-directory or is now empty.
if (int last = fs::remove(p, ec); !ec)
return count + last;
__last_system_error() noexcept
{
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+ // N.B. use error_code::default_error_condition() to convert to generic.
return {(int)::GetLastError(), std::system_category()};
#else
return {errno, std::generic_category()};