From: Joel Rosdahl Date: Sat, 23 Mar 2024 15:02:13 +0000 (+0100) Subject: enhance: Teach DirEntry how to stat a file descriptor X-Git-Tag: v4.10~69 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=837a873d1f84fdbdb0d431fc29db6635c84ac395;p=thirdparty%2Fccache.git enhance: Teach DirEntry how to stat a file descriptor --- diff --git a/src/ccache/util/DirEntry.cpp b/src/ccache/util/DirEntry.cpp index ec36098b..1877a069 100644 --- a/src/ccache/util/DirEntry.cpp +++ b/src/ccache/util/DirEntry.cpp @@ -250,15 +250,27 @@ DirEntry::size_on_disk() const const DirEntry::stat_t& DirEntry::do_stat() const { - if (!m_initialized) { - m_exists = false; - m_is_symlink = false; + if (m_initialized) { + return m_stat; + } - auto path = pstr(m_path); + m_exists = false; + m_is_symlink = false; - int result = lstat_func(path, &m_stat); + int result = 0; + +#ifndef _WIN32 + if (m_fd != -1) { + result = fstat(m_fd, &m_stat); + if (result == 0) { + m_exists = true; + } + } else +#endif + { + auto path = pstr(m_path); + result = lstat_func(path, &m_stat); if (result == 0) { - m_errno = 0; if (S_ISLNK(m_stat.st_mode) #ifdef _WIN32 || (m_stat.st_file_attributes & FILE_ATTRIBUTE_REPARSE_POINT) @@ -273,22 +285,32 @@ DirEntry::do_stat() const } else { m_exists = true; } - } else { - m_errno = errno; - if (m_log_on_error == LogOnError::yes) { - LOG("Failed to lstat {}: {}", m_path, strerror(m_errno)); - } } + } - if (!m_exists) { - // The file is missing, so just zero fill the stat structure. This will - // make e.g. the is_*() methods return false and mtime() will be 0, etc. - memset(&m_stat, '\0', sizeof(m_stat)); + if (result == 0) { + m_errno = 0; + } else { + m_errno = errno; + if (m_log_on_error == LogOnError::yes) { +#ifdef _WIN32 + LOG("Failed to stat {}: {}", m_path, strerror(m_errno)); +#else + LOG("Failed to {} {}: {}", + m_fd == -1 ? "stat" : "fstat", + m_path, + strerror(m_errno)); +#endif } + } - m_initialized = true; + if (!m_exists) { + // The file is missing, so just zero fill the stat structure. This will make + // the is_*() methods return false and mtime() will be 0, etc. + memset(&m_stat, '\0', sizeof(m_stat)); } + m_initialized = true; return m_stat; } diff --git a/src/ccache/util/DirEntry.hpp b/src/ccache/util/DirEntry.hpp index 90e02123..5f43cb1e 100644 --- a/src/ccache/util/DirEntry.hpp +++ b/src/ccache/util/DirEntry.hpp @@ -66,12 +66,25 @@ public: // 0. DirEntry() = default; + // Stat a path. + // // The underlying (l)stat(2) call will not be made by the constructor but // on-demand when calling the first query function. That (l)stat result is // then cached. See also the refresh method. DirEntry(const std::filesystem::path& path, LogOnError log_on_error = LogOnError::no); +#ifndef _WIN32 + // Stat an open file descriptor. + // + // The underlying fstat(2) call will not be made by the constructor but + // on-demand when calling the first query function. That fstat result is then + // cached. See also the refresh method. + DirEntry(const std::filesystem::path& path, + int fd, + LogOnError log_on_error = LogOnError::no); +#endif + // Return true if the file could be lstat(2)-ed (i.e., the directory entry // exists without following symlinks), otherwise false. operator bool() const; @@ -113,6 +126,9 @@ public: private: std::filesystem::path m_path; +#ifndef _WIN32 + int m_fd = -1; +#endif LogOnError m_log_on_error = LogOnError::no; mutable stat_t m_stat; mutable int m_errno = -1; @@ -130,6 +146,17 @@ inline DirEntry::DirEntry(const std::filesystem::path& path, { } +#ifndef _WIN32 +inline DirEntry::DirEntry(const std::filesystem::path& path, + int fd, + LogOnError log_on_error) + : m_path(path), + m_fd(fd), + m_log_on_error(log_on_error) +{ +} +#endif + inline DirEntry::operator bool() const { do_stat(); diff --git a/unittest/test_util_DirEntry.cpp b/unittest/test_util_DirEntry.cpp index d3b16462..9418a2f7 100644 --- a/unittest/test_util_DirEntry.cpp +++ b/unittest/test_util_DirEntry.cpp @@ -19,6 +19,7 @@ #include "TestUtil.hpp" #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include +#include #ifdef HAVE_UNISTD_H # include @@ -201,6 +203,23 @@ TEST_CASE("Construction for missing entry") #endif } +#ifndef _WIN32 +TEST_CASE("Stat file descriptor") +{ + TestContext test_context; + + util::write_file("a", "123"); + + util::Fd fd(open("a", O_RDONLY)); + DirEntry entry("a", *fd); + CHECK(entry); + CHECK(entry.exists()); + CHECK(!entry.is_symlink()); + CHECK(entry.size() == 3); + CHECK(entry.path() == "a"); +} +#endif + TEST_CASE("Caching and refresh") { TestContext test_context; @@ -665,6 +684,6 @@ TEST_CASE( CHECK(!(entry.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT)); CHECK(entry.reparse_tag() == 0); } -#endif +#endif // _WIN32 TEST_SUITE_END();