1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-07-05 04:01:47 +02:00

Merge pull request #13122 from Mic92/directory-iterator

Replace all instances of std::filesystem::directory_iterator with DirectoryIterator
This commit is contained in:
Jörg Thalheim 2025-05-01 14:22:21 +02:00 committed by GitHub
commit 143fb88ceb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 164 additions and 64 deletions

View file

@ -43,6 +43,34 @@ namespace fs {
}
}
DirectoryIterator::DirectoryIterator(const std::filesystem::path& p) {
try {
// **Attempt to create the underlying directory_iterator**
it_ = std::filesystem::directory_iterator(p);
} catch (const std::filesystem::filesystem_error& e) {
// **Catch filesystem_error and throw SysError**
// Adapt the error message as needed for SysError
throw SysError("cannot read directory %s", p);
}
}
DirectoryIterator& DirectoryIterator::operator++() {
// **Attempt to increment the underlying iterator**
std::error_code ec;
it_.increment(ec);
if (ec) {
// Try to get path info if possible, might fail if iterator is bad
try {
if (it_ != std::filesystem::directory_iterator{}) {
throw SysError("cannot read directory past %s: %s", it_->path(), ec.message());
}
} catch (...) {
throw SysError("cannot read directory");
}
}
return *this;
}
bool isAbsolute(PathView path)
{
return fs::path { path }.is_absolute();
@ -359,7 +387,7 @@ void recursiveSync(const Path & path)
while (!dirsToEnumerate.empty()) {
auto currentDir = dirsToEnumerate.back();
dirsToEnumerate.pop_back();
for (auto & entry : std::filesystem::directory_iterator(currentDir)) {
for (auto & entry : DirectoryIterator(currentDir)) {
auto st = entry.symlink_status();
if (fs::is_directory(st)) {
dirsToEnumerate.emplace_back(entry.path());
@ -663,7 +691,7 @@ void copyFile(const fs::path & from, const fs::path & to, bool andDelete)
fs::copy(from, to, fs::copy_options::copy_symlinks | fs::copy_options::overwrite_existing);
} else if (fs::is_directory(fromStatus)) {
fs::create_directory(to);
for (auto & entry : fs::directory_iterator(from)) {
for (auto & entry : DirectoryIterator(from)) {
copyFile(entry, to / entry.path().filename(), andDelete);
}
} else {

View file

@ -376,4 +376,64 @@ extern PathFilter defaultPathFilter;
*/
bool chmodIfNeeded(const std::filesystem::path & path, mode_t mode, mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO);
/**
* @brief A directory iterator that can be used to iterate over the
* contents of a directory. It is similar to std::filesystem::directory_iterator
* but throws NixError on failure instead of std::filesystem::filesystem_error.
*/
class DirectoryIterator {
public:
// --- Iterator Traits ---
using iterator_category = std::input_iterator_tag;
using value_type = std::filesystem::directory_entry;
using difference_type = std::ptrdiff_t;
using pointer = const std::filesystem::directory_entry*;
using reference = const std::filesystem::directory_entry&;
// Default constructor (represents end iterator)
DirectoryIterator() noexcept = default;
// Constructor taking a path
explicit DirectoryIterator(const std::filesystem::path& p);
reference operator*() const {
// Accessing the value itself doesn't typically throw filesystem_error
// after successful construction/increment, but underlying operations might.
// If directory_entry methods called via -> could throw, add try-catch there.
return *it_;
}
pointer operator->() const {
return &(*it_);
}
DirectoryIterator& operator++();
// Postfix increment operator
DirectoryIterator operator++(int) {
DirectoryIterator temp = *this;
++(*this); // Uses the prefix increment's try-catch logic
return temp;
}
// Equality comparison
friend bool operator==(const DirectoryIterator& a, const DirectoryIterator& b) noexcept {
return a.it_ == b.it_;
}
// Inequality comparison
friend bool operator!=(const DirectoryIterator& a, const DirectoryIterator& b) noexcept {
return !(a == b);
}
// Allow direct use in range-based for loops if iterating over an instance
DirectoryIterator begin() const { return *this; }
DirectoryIterator end() const { return DirectoryIterator{}; }
private:
std::filesystem::directory_iterator it_;
};
}

View file

@ -65,7 +65,7 @@ static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool retu
/* Otherwise, manually kill every process in the subcgroups and
this cgroup. */
for (auto & entry : std::filesystem::directory_iterator{cgroup}) {
for (auto & entry : DirectoryIterator{cgroup}) {
checkInterrupt();
if (entry.symlink_status().type() != std::filesystem::file_type::directory) continue;
destroyCgroup(cgroup / entry.path().filename(), false);

View file

@ -138,42 +138,38 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath &
{
assertNoSymlinks(path);
DirEntries res;
try {
for (auto & entry : std::filesystem::directory_iterator{makeAbsPath(path)}) {
checkInterrupt();
auto type = [&]() -> std::optional<Type> {
std::filesystem::file_type nativeType;
try {
nativeType = entry.symlink_status().type();
} catch (std::filesystem::filesystem_error & e) {
// We cannot always stat the child. (Ideally there is no
// stat because the native directory entry has the type
// already, but this isn't always the case.)
if (e.code() == std::errc::permission_denied || e.code() == std::errc::operation_not_permitted)
return std::nullopt;
else throw;
}
for (auto & entry : DirectoryIterator{makeAbsPath(path)}) {
checkInterrupt();
auto type = [&]() -> std::optional<Type> {
std::filesystem::file_type nativeType;
try {
nativeType = entry.symlink_status().type();
} catch (std::filesystem::filesystem_error & e) {
// We cannot always stat the child. (Ideally there is no
// stat because the native directory entry has the type
// already, but this isn't always the case.)
if (e.code() == std::errc::permission_denied || e.code() == std::errc::operation_not_permitted)
return std::nullopt;
else throw;
}
// cannot exhaustively enumerate because implementation-specific
// additional file types are allowed.
// cannot exhaustively enumerate because implementation-specific
// additional file types are allowed.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-enum"
switch (nativeType) {
case std::filesystem::file_type::regular: return Type::tRegular; break;
case std::filesystem::file_type::symlink: return Type::tSymlink; break;
case std::filesystem::file_type::directory: return Type::tDirectory; break;
case std::filesystem::file_type::character: return Type::tChar; break;
case std::filesystem::file_type::block: return Type::tBlock; break;
case std::filesystem::file_type::fifo: return Type::tFifo; break;
case std::filesystem::file_type::socket: return Type::tSocket; break;
default: return tUnknown;
}
#pragma GCC diagnostic pop
}();
res.emplace(entry.path().filename().string(), type);
switch (nativeType) {
case std::filesystem::file_type::regular: return Type::tRegular; break;
case std::filesystem::file_type::symlink: return Type::tSymlink; break;
case std::filesystem::file_type::directory: return Type::tDirectory; break;
case std::filesystem::file_type::character: return Type::tChar; break;
case std::filesystem::file_type::block: return Type::tBlock; break;
case std::filesystem::file_type::fifo: return Type::tFifo; break;
case std::filesystem::file_type::socket: return Type::tSocket; break;
default: return tUnknown;
}
} catch (std::filesystem::filesystem_error & e) {
throw SysError("reading directory %1%", showPath(path));
#pragma GCC diagnostic pop
}();
res.emplace(entry.path().filename().string(), type);
}
return res;
}

View file

@ -191,7 +191,7 @@ void unix::closeExtraFDs()
#ifdef __linux__
try {
for (auto & s : std::filesystem::directory_iterator{"/proc/self/fd"}) {
for (auto & s : DirectoryIterator{"/proc/self/fd"}) {
checkInterrupt();
auto fd = std::stoi(s.path().filename());
if (fd > MAX_KEPT_FD) {
@ -201,7 +201,6 @@ void unix::closeExtraFDs()
}
return;
} catch (SysError &) {
} catch (std::filesystem::filesystem_error &) {
}
#endif