From d3761b2f22aaf84238da8dffd3a42af977068242 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sun, 11 May 2025 13:33:24 +0300 Subject: [PATCH] Support sub-second file time-stamps on MS-Windows * nt/inc/sys/stat.h (struct stat): New members for nsec part of file times. * lib-src/ntlib.c (convert_time): * src/w32.c (convert_time): Accept an additional argument TIME_NSEC and set it to the sub-second part of time. All callers changed. (cherry picked from commit 3975094f1d96680fe73232ca4216733904ebecd0) --- lib-src/ntlib.c | 42 ++++++++++++++++++++++++++++++------------ nt/inc/sys/stat.h | 3 +++ src/w32.c | 44 ++++++++++++++++++++++++++++++++------------ 3 files changed, 65 insertions(+), 24 deletions(-) diff --git a/lib-src/ntlib.c b/lib-src/ntlib.c index bcb9a4bbab2..2ba8d09c1ec 100644 --- a/lib-src/ntlib.c +++ b/lib-src/ntlib.c @@ -30,6 +30,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #include #include #include @@ -256,7 +257,7 @@ static long double utc_base; static int init = 0; static time_t -convert_time (FILETIME ft) +convert_time (FILETIME ft, int *time_nsec) { long double ret; @@ -266,7 +267,8 @@ convert_time (FILETIME ft) ret = (long double) ft.dwHighDateTime * 4096.0L * 1024.0L * 1024.0L + ft.dwLowDateTime; ret -= utc_base; - return (time_t) (ret * 1e-7L); + *time_nsec = (int) fmodl (ret, 1.0e7L) * 100; + return (time_t) (ret * 1.0e-7L); } static int @@ -373,11 +375,19 @@ stat (const char * path, struct stat * buf) buf->st_size += wfd.nFileSizeLow; /* Convert timestamps to Unix format. */ - buf->st_mtime = convert_time (wfd.ftLastWriteTime); - buf->st_atime = convert_time (wfd.ftLastAccessTime); - if (buf->st_atime == 0) buf->st_atime = buf->st_mtime; - buf->st_ctime = convert_time (wfd.ftCreationTime); - if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime; + buf->st_mtime = convert_time (wfd.ftLastWriteTime, &buf->st_mtimensec); + buf->st_atime = convert_time (wfd.ftLastAccessTime, &buf->st_atimensec); + if (buf->st_atime == 0) + { + buf->st_atime = buf->st_mtime; + buf->st_atimensec = buf->st_mtimensec; + } + buf->st_ctime = convert_time (wfd.ftCreationTime, &buf->st_ctimensec); + if (buf->st_ctime == 0) + { + buf->st_ctime = buf->st_mtime; + buf->st_ctimensec = buf->st_mtimensec; + } /* determine rwx permissions */ if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) @@ -473,11 +483,19 @@ fstat (int desc, struct stat * buf) buf->st_size += info.nFileSizeLow; /* Convert timestamps to Unix format. */ - buf->st_mtime = convert_time (info.ftLastWriteTime); - buf->st_atime = convert_time (info.ftLastAccessTime); - if (buf->st_atime == 0) buf->st_atime = buf->st_mtime; - buf->st_ctime = convert_time (info.ftCreationTime); - if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime; + buf->st_mtime = convert_time (info.ftLastWriteTime, &buf->st_mtimensec); + buf->st_atime = convert_time (info.ftLastAccessTime, &buf->st_atimensec); + if (buf->st_atime == 0) + { + buf->st_atime = buf->st_mtime; + buf->st_atimensec = buf->st_mtimensec; + } + buf->st_ctime = convert_time (info.ftCreationTime, &buf->st_ctimensec); + if (buf->st_ctime == 0) + { + buf->st_ctime = buf->st_mtime; + buf->st_ctimensec = buf->st_mtimensec; + } /* determine rwx permissions */ if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) diff --git a/nt/inc/sys/stat.h b/nt/inc/sys/stat.h index 3aa70f3052e..c93923fd9e8 100644 --- a/nt/inc/sys/stat.h +++ b/nt/inc/sys/stat.h @@ -108,6 +108,9 @@ struct stat { time_t st_ctime; char st_uname[260]; char st_gname[260]; + int st_atimensec; + int st_mtimensec; + int st_ctimensec; }; /* These are here to avoid compiler warnings when using wchar.h. */ diff --git a/src/w32.c b/src/w32.c index d7bf173ce25..c504c141b31 100644 --- a/src/w32.c +++ b/src/w32.c @@ -5076,9 +5076,10 @@ initialize_utc_base (void) } static time_t -convert_time (FILETIME ft) +convert_time (FILETIME ft, int *time_nsec) { ULONGLONG tmp; + time_t time_sec; if (!init) { @@ -5090,7 +5091,10 @@ convert_time (FILETIME ft) return 0; FILETIME_TO_U64 (tmp, ft); - return (time_t) ((tmp - utc_base) / 10000000L); + tmp -= utc_base; + time_sec = (time_t) (tmp / 10000000L); + *time_nsec = (tmp - (ULONGLONG) time_sec * 10000000L) * 100L; + return time_sec; } static void @@ -5707,11 +5711,19 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks) buf->st_nlink = nlinks; /* Convert timestamps to Unix format. */ - buf->st_mtime = convert_time (wtime); - buf->st_atime = convert_time (atime); - if (buf->st_atime == 0) buf->st_atime = buf->st_mtime; - buf->st_ctime = convert_time (ctime); - if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime; + buf->st_mtime = convert_time (wtime, &buf->st_mtimensec); + buf->st_atime = convert_time (atime, &buf->st_atimensec); + if (buf->st_atime == 0) + { + buf->st_atime = buf->st_mtime; + buf->st_atimensec = buf->st_mtimensec; + } + buf->st_ctime = convert_time (ctime, &buf->st_ctimensec); + if (buf->st_ctime == 0) + { + buf->st_ctime = buf->st_mtime; + buf->st_ctimensec = buf->st_mtimensec; + } /* determine rwx permissions */ if (is_a_symlink && !follow_symlinks) @@ -5853,11 +5865,19 @@ fstat (int desc, struct stat * buf) buf->st_size += info.nFileSizeLow; /* Convert timestamps to Unix format. */ - buf->st_mtime = convert_time (info.ftLastWriteTime); - buf->st_atime = convert_time (info.ftLastAccessTime); - if (buf->st_atime == 0) buf->st_atime = buf->st_mtime; - buf->st_ctime = convert_time (info.ftCreationTime); - if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime; + buf->st_mtime = convert_time (info.ftLastWriteTime, &buf->st_mtimensec); + buf->st_atime = convert_time (info.ftLastAccessTime, &buf->st_atimensec); + if (buf->st_atime == 0) + { + buf->st_atime = buf->st_mtime; + buf->st_atimensec = buf->st_mtimensec; + } + buf->st_ctime = convert_time (info.ftCreationTime, &buf->st_ctimensec); + if (buf->st_ctime == 0) + { + buf->st_ctime = buf->st_mtime; + buf->st_ctimensec = buf->st_mtimensec; + } /* determine rwx permissions */ if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) -- 2.39.5