Browse Source

Add mtime/atime/ctime with nanoseconds to TFileStat.
3523ab3f5aade2bdf4c0efd5dd2defbe19f124ff

sath 10 months ago
parent
commit
7b31bb09a8
4 changed files with 94 additions and 43 deletions
  1. 32 8
      util/datetime/systime.cpp
  2. 4 0
      util/datetime/systime.h
  3. 49 29
      util/system/fstat.cpp
  4. 9 6
      util/system/fstat.h

+ 32 - 8
util/datetime/systime.cpp

@@ -5,15 +5,39 @@
 
 #ifdef _win_
 
+namespace {
+    // Number of 100 nanosecond units from 1/1/1601 to 1/1/1970
+    constexpr ui64 NUMBER_OF_100_NANO_BETWEEN_1601_1970 =
+        ULL(116444736000000000);
+    constexpr ui64 NUMBER_OF_100_NANO_IN_SECOND = ULL(10000000);
+
+    union TFTUnion {
+        ui64 FTScalar;
+        FILETIME FTStruct;
+    };
+} // namespace
+
 void FileTimeToTimeval(const FILETIME* ft, timeval* tv) {
-    const i64 NANOINTERVAL = LL(116444736000000000); // Number of 100 nanosecond units from 1/1/1601 to 1/1/1970
-    union {
-        ui64 ft_scalar;
-        FILETIME ft_struct;
-    } nt_time;
-    nt_time.ft_struct = *ft;
-    tv->tv_sec = (long)((nt_time.ft_scalar - NANOINTERVAL) / LL(10000000));
-    tv->tv_usec = (i32)((nt_time.ft_scalar / LL(10)) % LL(1000000));
+    Y_ASSERT(ft);
+    Y_ASSERT(tv);
+    TFTUnion ntTime;
+    ntTime.FTStruct = *ft;
+    ntTime.FTScalar -= NUMBER_OF_100_NANO_BETWEEN_1601_1970;
+    tv->tv_sec =
+        static_cast<long>(ntTime.FTScalar / NUMBER_OF_100_NANO_IN_SECOND);
+    tv->tv_usec = static_cast<long>(
+        (ntTime.FTScalar % NUMBER_OF_100_NANO_IN_SECOND) / LL(10));
+}
+
+void FileTimeToTimespec(const FILETIME& ft, struct timespec* ts) {
+    Y_ASSERT(ts);
+    TFTUnion ntTime;
+    ntTime.FTStruct = ft;
+    ntTime.FTScalar -= NUMBER_OF_100_NANO_BETWEEN_1601_1970;
+    ts->tv_sec =
+        static_cast<time_t>(ntTime.FTScalar / NUMBER_OF_100_NANO_IN_SECOND);
+    ts->tv_nsec = static_cast<long>(
+        (ntTime.FTScalar % NUMBER_OF_100_NANO_IN_SECOND) * LL(100));
 }
 
 int gettimeofday(timeval* tp, void*) {

+ 4 - 0
util/datetime/systime.h

@@ -15,8 +15,12 @@ TString CTimeR(const time_t* timer);
     #include <util/system/winint.h>
     #include <winsock2.h>
 
+// Convert FILETIME to timeval - seconds and microseconds.
 void FileTimeToTimeval(const FILETIME* ft, struct timeval* tv);
 
+// Convert FILETIME to timespec - seconds and nanoseconds.
+void FileTimeToTimespec(const FILETIME& ft, struct timespec* ts);
+
 // obtains the current time, expressed as seconds and microseconds since 00:00 UTC, January 1, 1970
 int gettimeofday(struct timeval* tp, void*);
 

+ 49 - 29
util/system/fstat.cpp

@@ -69,34 +69,61 @@ using TSystemFStat = struct stat;
     #error unsupported platform
 #endif
 
+#if defined(_unix_)
 static void MakeStatFromStructStat(TFileStat& st, const struct stat& fs) {
     st.Mode = fs.st_mode;
     st.NLinks = fs.st_nlink;
     st.Uid = fs.st_uid;
     st.Gid = fs.st_gid;
     st.Size = fs.st_size;
-#ifdef _unix_
     st.AllocationSize = fs.st_blocks * 512;
-#else
-    st.AllocationSize = st.Size; // FIXME
-#endif
+
+    #if defined(_linux_)
+    st.ATime = fs.st_atim.tv_sec;
+    st.ATimeNSec = fs.st_atim.tv_nsec;
+
+    st.MTime = fs.st_mtim.tv_sec;
+    st.MTimeNSec = fs.st_mtim.tv_nsec;
+
+    st.CTime = fs.st_ctim.tv_sec;
+    st.CTimeNSec = fs.st_ctim.tv_nsec;
+    #elif defined(_darwin_)
+    st.ATime = fs.st_atimespec.tv_sec;
+    st.ATimeNSec = fs.st_atimespec.tv_nsec;
+
+    st.MTime = fs.st_mtimespec.tv_sec;
+    st.MTimeNSec = fs.st_mtimespec.tv_nsec;
+
+    st.CTime = fs.st_birthtimespec.tv_sec;
+    st.CTimeNSec = fs.st_birthtimespec.tv_nsec;
+    #else
+    // Fallback.
     st.ATime = fs.st_atime;
     st.MTime = fs.st_mtime;
     st.CTime = fs.st_ctime;
+    #endif
+
     st.INode = fs.st_ino;
 }
+#endif
 
 static void MakeStat(TFileStat& st, const TSystemFStat& fs) {
 #ifdef _unix_
     MakeStatFromStructStat(st, fs);
 #else
-    timeval tv;
-    FileTimeToTimeval(&fs.ftCreationTime, &tv);
-    st.CTime = tv.tv_sec;
-    FileTimeToTimeval(&fs.ftLastAccessTime, &tv);
-    st.ATime = tv.tv_sec;
-    FileTimeToTimeval(&fs.ftLastWriteTime, &tv);
-    st.MTime = tv.tv_sec;
+    timespec timeSpec;
+    FileTimeToTimespec(fs.ftCreationTime, &timeSpec);
+    st.CTime = timeSpec.tv_sec;
+    st.CTimeNSec = timeSpec.tv_nsec;
+
+    FileTimeToTimespec(fs.ftLastAccessTime, &timeSpec);
+    st.ATime = timeSpec.tv_sec;
+    st.ATimeNSec = timeSpec.tv_nsec;
+
+    FileTimeToTimespec(fs.ftLastWriteTime, &timeSpec);
+    st.MTime = timeSpec.tv_sec;
+    st.MTimeNSec = timeSpec.tv_nsec;
+
     st.NLinks = fs.nNumberOfLinks;
     st.Mode = GetFileMode(fs.dwFileAttributes, fs.ReparseTag);
     st.Uid = 0;
@@ -123,9 +150,13 @@ static bool GetStatByHandle(TSystemFStat& fs, FHANDLE f) {
 
 static bool GetStatByName(TSystemFStat& fs, const char* fileName, bool nofollow) {
 #ifdef _win_
-    TFileHandle h = NFsPrivate::CreateFileWithUtf8Name(fileName, FILE_READ_ATTRIBUTES | FILE_READ_EA, FILE_SHARE_READ | FILE_SHARE_WRITE,
-                                                       OPEN_EXISTING,
-                                                       (nofollow ? FILE_FLAG_OPEN_REPARSE_POINT : 0) | FILE_FLAG_BACKUP_SEMANTICS, true);
+    TFileHandle h = NFsPrivate::CreateFileWithUtf8Name(
+        fileName,
+        FILE_READ_ATTRIBUTES | FILE_READ_EA,
+        FILE_SHARE_READ | FILE_SHARE_WRITE,
+        OPEN_EXISTING,
+        (nofollow ? FILE_FLAG_OPEN_REPARSE_POINT : 0) | FILE_FLAG_BACKUP_SEMANTICS,
+        true);
     if (!h.IsOpen()) {
         return false;
     }
@@ -150,9 +181,13 @@ TFileStat::TFileStat(FHANDLE f) {
     }
 }
 
+#if defined(_unix_)
 TFileStat::TFileStat(const struct stat& st) {
     MakeStatFromStructStat(*this, st);
 }
+#endif
+
+bool TFileStat::operator==(const TFileStat& other) const noexcept = default;
 
 void TFileStat::MakeFromFileName(const char* fileName, bool nofollow) {
     TSystemFStat st;
@@ -191,21 +226,6 @@ bool TFileStat::IsSymlink() const noexcept {
     return S_ISLNK(Mode);
 }
 
-bool operator==(const TFileStat& l, const TFileStat& r) noexcept {
-    return l.Mode == r.Mode &&
-           l.Uid == r.Uid &&
-           l.Gid == r.Gid &&
-           l.NLinks == r.NLinks &&
-           l.Size == r.Size &&
-           l.ATime == r.ATime &&
-           l.MTime == r.MTime &&
-           l.CTime == r.CTime;
-}
-
-bool operator!=(const TFileStat& l, const TFileStat& r) noexcept {
-    return !(l == r);
-}
-
 i64 GetFileLength(FHANDLE fd) {
 #if defined(_win_)
     LARGE_INTEGER pos;

+ 9 - 6
util/system/fstat.h

@@ -17,11 +17,13 @@ struct TFileStat {
     ui64 INode = 0;          /* inode number */
     ui64 AllocationSize = 0; /* number of bytes allocated on the disk */
 
-    time_t ATime = 0; /* time of last access */
-    time_t MTime = 0; /* time of last modification */
-    time_t CTime = 0; /* time of last status change */
+    time_t ATime = 0;   /* time of last access */
+    long ATimeNSec = 0; /* nsec of last access */
+    time_t MTime = 0;   /* time of last modification */
+    long MTimeNSec = 0; /* nsec of last modification */
+    time_t CTime = 0;   /* time of last status change */
+    long CTimeNSec = 0; /* nsec of last status change */
 
-public:
     TFileStat();
 
     bool IsNull() const noexcept;
@@ -30,15 +32,16 @@ public:
     bool IsDir() const noexcept;
     bool IsSymlink() const noexcept;
 
+#if defined(_unix_)
     explicit TFileStat(const struct stat& fs);
+#endif
     explicit TFileStat(const TFile& f);
     explicit TFileStat(FHANDLE f);
     TFileStat(const TFsPath& fileName, bool nofollow = false);
     TFileStat(const TString& fileName, bool nofollow = false);
     TFileStat(const char* fileName, bool nofollow = false);
 
-    friend bool operator==(const TFileStat& l, const TFileStat& r) noexcept;
-    friend bool operator!=(const TFileStat& l, const TFileStat& r) noexcept;
+    bool operator==(const TFileStat& other) const noexcept;
 
 private:
     void MakeFromFileName(const char* fileName, bool nofollow);