]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
enhance: Teach DirEntry how to stat a file descriptor
authorJoel Rosdahl <joel@rosdahl.net>
Sat, 23 Mar 2024 15:02:13 +0000 (16:02 +0100)
committerJoel Rosdahl <joel@rosdahl.net>
Sun, 24 Mar 2024 08:27:23 +0000 (09:27 +0100)
src/ccache/util/DirEntry.cpp
src/ccache/util/DirEntry.hpp
unittest/test_util_DirEntry.cpp

index ec36098b1836167bea45977bc7c0ef19b911f323..1877a069d4a6935ecf259be60c29b7a7edfecff0 100644 (file)
@@ -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;
 }
 
index 90e02123f74f76d8a2a27f6246b3172c50f75ff6..5f43cb1e06abc0fd1e7fdf2712d46c245a1f72a0 100644 (file)
@@ -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();
index d3b16462c8cf05c81a67715a421eaaa6ceb996bb..9418a2f75e4e44ad4cd88496259c1452c407ca53 100644 (file)
@@ -19,6 +19,7 @@
 #include "TestUtil.hpp"
 
 #include <ccache/util/DirEntry.hpp>
+#include <ccache/util/Fd.hpp>
 #include <ccache/util/Finalizer.hpp>
 #include <ccache/util/environment.hpp>
 #include <ccache/util/file.hpp>
@@ -26,6 +27,7 @@
 #include <ccache/util/wincompat.hpp>
 
 #include <doctest.h>
+#include <fcntl.h>
 
 #ifdef HAVE_UNISTD_H
 #  include <unistd.h>
@@ -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();