1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-25 14:51:16 +02:00

add DirectoryIterator to re-throw std::filesystem::filesystem_error

Co-authored-by: Sergei Zimmerman <145775305+xokdvium@users.noreply.github.com>
(cherry picked from commit 7ccc0d591f)
This commit is contained in:
Jörg Thalheim 2025-05-01 09:50:53 +02:00
parent 8de4c272dc
commit 0f4b17e51f
3 changed files with 126 additions and 0 deletions

View file

@ -275,4 +275,26 @@ TEST(makeParentCanonical, root)
{ {
ASSERT_EQ(makeParentCanonical("/"), "/"); ASSERT_EQ(makeParentCanonical("/"), "/");
} }
/* ----------------------------------------------------------------------------
* DirectoryIterator
* --------------------------------------------------------------------------*/
TEST(DirectoryIterator, works)
{
auto tmpDir = nix::createTempDir();
nix::AutoDelete delTmpDir(tmpDir, true);
nix::writeFile(tmpDir + "/somefile", "");
for (auto path : DirectoryIterator(tmpDir)) {
ASSERT_EQ(path.path().string(), tmpDir + "/somefile");
}
}
TEST(DirectoryIterator, nonexistent)
{
ASSERT_THROW(DirectoryIterator("/schnitzel/darmstadt/pommes"), SysError);
}
} }

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) bool isAbsolute(PathView path)
{ {
return fs::path { path }.is_absolute(); return fs::path { path }.is_absolute();

View file

@ -360,4 +360,80 @@ typedef std::function<bool(const Path & path)> PathFilter;
extern PathFilter defaultPathFilter; extern PathFilter defaultPathFilter;
/**
* Change permissions of a file only if necessary.
*
* @details
* Skip chmod call if the directory already has the requested permissions.
* This is to avoid failing when the executing user lacks permissions to change the
* directory's permissions even if it would be no-op.
*
* @param path Path to the file to change the permissions for.
* @param mode New file mode.
* @param mask Used for checking if the file already has requested permissions.
*
* @return true if permissions changed, false otherwise.
*/
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_;
};
} }