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.
This commit is contained in:
Eli Zaretskii 2025-05-11 13:33:24 +03:00
parent 9df2074a06
commit 3975094f1d
3 changed files with 65 additions and 24 deletions

View file

@ -30,6 +30,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <direct.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <math.h>
#include <errno.h>
#include <ctype.h>
#include <sys/timeb.h>
@ -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)

View file

@ -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. */

View file

@ -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)