From: Chen-Yu Tsai <w...@csie.org>

Since Linux 2.6 the stat syscalls have mostly supported nanosecond
components for each of the file-related timestamps.

QEMU user mode emulation currently does not pass through the nanosecond
portion of the timestamp, even when the host system fills in the value.
This results in a mismatch when run on subsecond resolution filesystems
such as ext4 or XFS.

An example of this leading to inconsistency is cross-debootstraping a
full desktop root filesystem of Debian Buster. Recent versions of
fontconfig store the full timestamp (instead of just the second portion)
of the directory in its per-directory cache file, and checks this against
the directory to see if the cache is up-to-date. With QEMU user mode
emulation, the timestamp stored is incorrect, and upon booting the rootfs
natively, fontconfig discovers the mismatch, and proceeds to rebuild the
cache on the comparatively slow machine (low-power ARM vs x86). This
stalls the first attempt to open whatever application that incorporates
fontconfig.

This patch renames the "unused" padding trailing each timestamp element
to its nanosecond counterpart name if such an element exists in the
kernel sources for the given platform. Not all do. Then have the syscall
wrapper fill in the nanosecond portion if the host supports it, as
specified by the _POSIX_C_SOURCE and _XOPEN_SOURCE feature macros.

Recent versions of glibc only use stat64 and newfstatat syscalls on
32-bit and 64-bit platforms respectively. The changes in this patch
were tested by directly calling the stat, stat64 and newfstatat syscalls
directly, in addition to the glibc wrapper, on arm and aarch64 little
endian targets.

Reviewed-by: Laurent Vivier <laur...@vivier.eu>
Signed-off-by: Chen-Yu Tsai <w...@csie.org>
Message-Id: <20190522162147.26303-1-w...@kernel.org>
Signed-off-by: Laurent Vivier <laur...@vivier.eu>
---
 linux-user/syscall.c      | 19 +++++++++++++++
 linux-user/syscall_defs.h | 49 +++++++++++++++++++++++++--------------
 2 files changed, 50 insertions(+), 18 deletions(-)

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index e43255c29e56..e311fcda0517 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -6432,6 +6432,11 @@ static inline abi_long host_to_target_stat64(void 
*cpu_env,
         __put_user(host_st->st_atime, &target_st->target_st_atime);
         __put_user(host_st->st_mtime, &target_st->target_st_mtime);
         __put_user(host_st->st_ctime, &target_st->target_st_ctime);
+#if _POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700
+        __put_user(host_st->st_atim.tv_nsec, &target_st->target_st_atime_nsec);
+        __put_user(host_st->st_mtim.tv_nsec, &target_st->target_st_mtime_nsec);
+        __put_user(host_st->st_ctim.tv_nsec, &target_st->target_st_ctime_nsec);
+#endif
         unlock_user_struct(target_st, target_addr, 1);
     } else
 #endif
@@ -6462,6 +6467,11 @@ static inline abi_long host_to_target_stat64(void 
*cpu_env,
         __put_user(host_st->st_atime, &target_st->target_st_atime);
         __put_user(host_st->st_mtime, &target_st->target_st_mtime);
         __put_user(host_st->st_ctime, &target_st->target_st_ctime);
+#if _POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700
+        __put_user(host_st->st_atim.tv_nsec, &target_st->target_st_atime_nsec);
+        __put_user(host_st->st_mtim.tv_nsec, &target_st->target_st_mtime_nsec);
+        __put_user(host_st->st_ctim.tv_nsec, &target_st->target_st_ctime_nsec);
+#endif
         unlock_user_struct(target_st, target_addr, 1);
     }
 
@@ -8915,6 +8925,15 @@ static abi_long do_syscall1(void *cpu_env, int num, 
abi_long arg1,
                 __put_user(st.st_atime, &target_st->target_st_atime);
                 __put_user(st.st_mtime, &target_st->target_st_mtime);
                 __put_user(st.st_ctime, &target_st->target_st_ctime);
+#if (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700) && \
+    defined(TARGET_STAT_HAVE_NSEC)
+                __put_user(st.st_atim.tv_nsec,
+                           &target_st->target_st_atime_nsec);
+                __put_user(st.st_mtim.tv_nsec,
+                           &target_st->target_st_mtime_nsec);
+                __put_user(st.st_ctim.tv_nsec,
+                           &target_st->target_st_ctime_nsec);
+#endif
                 unlock_user_struct(target_st, arg2, 1);
             }
         }
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index cb40620114f8..7f141f699c1a 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -1185,6 +1185,7 @@ struct target_winsize {
 #if (defined(TARGET_I386) && defined(TARGET_ABI32)) \
     || (defined(TARGET_ARM) && defined(TARGET_ABI32)) \
     || defined(TARGET_CRIS)
+#define TARGET_STAT_HAVE_NSEC
 struct target_stat {
        unsigned short st_dev;
        unsigned short __pad1;
@@ -1199,11 +1200,11 @@ struct target_stat {
        abi_ulong  st_blksize;
        abi_ulong  st_blocks;
        abi_ulong  target_st_atime;
-       abi_ulong  __unused1;
+       abi_ulong  target_st_atime_nsec;
        abi_ulong  target_st_mtime;
-       abi_ulong  __unused2;
+       abi_ulong  target_st_mtime_nsec;
        abi_ulong  target_st_ctime;
-       abi_ulong  __unused3;
+       abi_ulong  target_st_ctime_nsec;
        abi_ulong  __unused4;
        abi_ulong  __unused5;
 };
@@ -1235,13 +1236,13 @@ struct target_stat64 {
        abi_ulong       __pad4;         /* future possible st_blocks high bits 
*/
 
        abi_ulong       target_st_atime;
-       abi_ulong       __pad5;
+       abi_ulong       target_st_atime_nsec;
 
        abi_ulong       target_st_mtime;
-       abi_ulong       __pad6;
+       abi_ulong       target_st_mtime_nsec;
 
        abi_ulong       target_st_ctime;
-       abi_ulong       __pad7;         /* will be high 32 bits of ctime 
someday */
+       abi_ulong       target_st_ctime_nsec;
 
        unsigned long long      st_ino;
 } QEMU_PACKED;
@@ -1320,19 +1321,20 @@ struct target_stat64 {
        unsigned int    st_blocks;
 
        abi_ulong       target_st_atime;
-       abi_ulong       __unused1;
+       abi_ulong       target_st_atime_nsec;
 
        abi_ulong       target_st_mtime;
-       abi_ulong       __unused2;
+       abi_ulong       target_st_mtime_nsec;
 
        abi_ulong       target_st_ctime;
-       abi_ulong       __unused3;
+       abi_ulong       target_st_ctime_nsec;
 
        abi_ulong       __unused4[3];
 };
 
 #elif defined(TARGET_SPARC)
 
+#define TARGET_STAT_HAVE_NSEC
 struct target_stat {
        unsigned short  st_dev;
        abi_ulong       st_ino;
@@ -1343,14 +1345,14 @@ struct target_stat {
        unsigned short  st_rdev;
        abi_long        st_size;
        abi_long        target_st_atime;
-       abi_ulong       __unused1;
+       abi_ulong       target_st_atime_nsec;
        abi_long        target_st_mtime;
-       abi_ulong       __unused2;
+       abi_ulong       target_st_mtime_nsec;
        abi_long        target_st_ctime;
-       abi_ulong       __unused3;
+       abi_ulong       target_st_ctime_nsec;
        abi_long        st_blksize;
        abi_long        st_blocks;
-       abi_ulong       __unused4[2];
+       abi_ulong       __unused1[2];
 };
 
 #define TARGET_HAS_STRUCT_STAT64
@@ -1378,20 +1380,21 @@ struct target_stat64 {
        unsigned int    st_blocks;
 
        unsigned int    target_st_atime;
-       unsigned int    __unused1;
+       unsigned int    target_st_atime_nsec;
 
        unsigned int    target_st_mtime;
-       unsigned int    __unused2;
+       unsigned int    target_st_mtime_nsec;
 
        unsigned int    target_st_ctime;
-       unsigned int    __unused3;
+       unsigned int    target_st_ctime_nsec;
 
-       unsigned int    __unused4;
-       unsigned int    __unused5;
+       unsigned int    __unused1;
+       unsigned int    __unused2;
 };
 
 #elif defined(TARGET_PPC)
 
+#define TARGET_STAT_HAVE_NSEC
 struct target_stat {
        abi_ulong st_dev;
        abi_ulong st_ino;
@@ -1449,6 +1452,7 @@ struct QEMU_PACKED target_stat64 {
 
 #elif defined(TARGET_MICROBLAZE)
 
+#define TARGET_STAT_HAVE_NSEC
 struct target_stat {
        abi_ulong st_dev;
        abi_ulong st_ino;
@@ -1564,6 +1568,7 @@ struct target_stat64 {
 
 #elif defined(TARGET_ABI_MIPSN64)
 
+#define TARGET_STAT_HAVE_NSEC
 /* The memory layout is the same as of struct stat64 of the 32-bit kernel.  */
 struct target_stat {
        unsigned int            st_dev;
@@ -1603,6 +1608,7 @@ struct target_stat {
 
 #elif defined(TARGET_ABI_MIPSN32)
 
+#define TARGET_STAT_HAVE_NSEC
 struct target_stat {
         abi_ulong    st_dev;
         abi_ulong    st_pad0[3]; /* Reserved for st_dev expansion */
@@ -1627,6 +1633,7 @@ struct target_stat {
 
 #elif defined(TARGET_ABI_MIPSO32)
 
+#define TARGET_STAT_HAVE_NSEC
 struct target_stat {
        unsigned        st_dev;
        abi_long        st_pad1[3];             /* Reserved for network id */
@@ -1743,6 +1750,7 @@ struct target_stat64 {
 
 #elif defined(TARGET_SH4)
 
+#define TARGET_STAT_HAVE_NSEC
 struct target_stat {
        abi_ulong  st_dev;
        abi_ulong  st_ino;
@@ -1802,6 +1810,7 @@ struct QEMU_PACKED target_stat64 {
 };
 
 #elif defined(TARGET_I386) && !defined(TARGET_ABI32)
+#define TARGET_STAT_HAVE_NSEC
 struct target_stat {
        abi_ulong       st_dev;
        abi_ulong       st_ino;
@@ -1847,6 +1856,7 @@ struct target_stat {
     abi_ulong  __unused[3];
 };
 #elif defined(TARGET_AARCH64)
+#define TARGET_STAT_HAVE_NSEC
 struct target_stat {
     abi_ulong  st_dev;
     abi_ulong  st_ino;
@@ -1869,6 +1879,7 @@ struct target_stat {
     unsigned int __unused[2];
 };
 #elif defined(TARGET_XTENSA)
+#define TARGET_STAT_HAVE_NSEC
 struct target_stat {
     abi_ulong       st_dev;
     abi_ulong       st_ino;
@@ -1918,6 +1929,7 @@ struct target_stat64  {
 
 /* These are the asm-generic versions of the stat and stat64 structures */
 
+#define TARGET_STAT_HAVE_NSEC
 struct target_stat {
     abi_ulong st_dev;
     abi_ulong st_ino;
@@ -1969,6 +1981,7 @@ struct target_stat64 {
 
 #elif defined(TARGET_HPPA)
 
+#define TARGET_STAT_HAVE_NSEC
 struct target_stat {
     abi_uint   st_dev;
     abi_uint   st_ino;
-- 
2.20.1


Reply via email to