From 243cdc175548c456661aabde4025a6ee2106b369 Mon Sep 17 00:00:00 2001 From: Andrey Gursky Date: Wed, 19 Apr 2017 01:59:24 +0200 Subject: [PATCH 1/2] Ticket #3821: initialize struct stat st_[acm]tim.tv_nsec when present struct stat in libc for Linux kernel contains few fields more since 14+ years [1]. From bits/stat.h: ``` struct timespec st_atim; /* Time of last access. */ struct timespec st_mtim; /* Time of last modification. */ struct timespec st_ctim; /* Time of last status change. */ # define st_atime st_atim.tv_sec /* Backward compatibility. */ # define st_mtime st_mtim.tv_sec # define st_ctime st_ctim.tv_sec ``` The conventional fields became an alias. POSIX.1-2008 made struct stat st_[acm]tim mandatory [2]. OS takes care to initialize struct stat properly [3]. By not using an OS syscall or a libc wrapper to fill struct stat, we have to take care of initializing all fields (or at least those being used later) explicitly. [1]: https://www.sourceware.org/ml/libc-alpha/2002-12/msg00011.html [2]: https://www.sourceware.org/ml/libc-alpha/2009-11/msg00102.html [3]: https://www.sourceware.org/ml/libc-alpha/2002-12/msg00013.html Fixes: file timestamps not preserved (https://mail.gnome.org/archives/mc-devel/2017-April/msg00000.html) Reported-By: Nerijus Baliunas Signed-off-by: Yury V. Zaytsev --- configure.ac | 2 +- src/vfs/cpio/cpio.c | 15 +++++++++++++++ src/vfs/tar/tar.c | 3 +++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 45836fcaf..387110daf 100644 --- a/configure.ac +++ b/configure.ac @@ -181,7 +181,7 @@ AC_TYPE_PID_T AC_TYPE_UID_T AC_STRUCT_ST_BLOCKS -AC_CHECK_MEMBERS([struct stat.st_blksize, struct stat.st_rdev]) +AC_CHECK_MEMBERS([struct stat.st_blksize, struct stat.st_rdev, struct stat.st_mtim]) gl_STAT_SIZE AH_TEMPLATE([sig_atomic_t], diff --git a/src/vfs/cpio/cpio.c b/src/vfs/cpio/cpio.c index dba1a07c3..ffe6267cf 100644 --- a/src/vfs/cpio/cpio.c +++ b/src/vfs/cpio/cpio.c @@ -462,9 +462,15 @@ cpio_create_entry (struct vfs_class *me, struct vfs_s_super *super, struct stat entry->ino->st.st_mode = st->st_mode; entry->ino->st.st_uid = st->st_uid; entry->ino->st.st_gid = st->st_gid; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + entry->ino->st.st_atim = st->st_atim; + entry->ino->st.st_mtim = st->st_mtim; + entry->ino->st.st_ctim = st->st_ctim; +#else entry->ino->st.st_atime = st->st_atime; entry->ino->st.st_mtime = st->st_mtime; entry->ino->st.st_ctime = st->st_ctime; +#endif } g_free (name); @@ -589,6 +595,9 @@ cpio_read_bin_head (struct vfs_class *me, struct vfs_s_super *super) st.st_rdev = u.buf.c_rdev; #endif st.st_size = (u.buf.c_filesizes[0] << 16) | u.buf.c_filesizes[1]; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + st.st_atim.tv_nsec = st.st_mtim.tv_nsec = st.st_ctim.tv_nsec = 0; +#endif st.st_atime = st.st_mtime = st.st_ctime = (u.buf.c_mtimes[0] << 16) | u.buf.c_mtimes[1]; return cpio_create_entry (me, super, &st, name); @@ -658,6 +667,9 @@ cpio_read_oldc_head (struct vfs_class *me, struct vfs_s_super *super) u.st.st_rdev = hd.c_rdev; #endif u.st.st_size = hd.c_filesize; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + u.st.st_atim.tv_nsec = u.st.st_mtim.tv_nsec = u.st.st_ctim.tv_nsec = 0; +#endif u.st.st_atime = u.st.st_mtime = u.st.st_ctime = hd.c_mtime; return cpio_create_entry (me, super, &u.st, name); @@ -736,6 +748,9 @@ cpio_read_crc_head (struct vfs_class *me, struct vfs_s_super *super) u.st.st_rdev = makedev (hd.c_rdev, hd.c_rdevmin); #endif u.st.st_size = hd.c_filesize; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + u.st.st_atim.tv_nsec = u.st.st_mtim.tv_nsec = u.st.st_ctim.tv_nsec = 0; +#endif u.st.st_atime = u.st.st_mtime = u.st.st_ctime = hd.c_mtime; return cpio_create_entry (me, super, &u.st, name); diff --git a/src/vfs/tar/tar.c b/src/vfs/tar/tar.c index 5e34e17f8..80cdc91e1 100644 --- a/src/vfs/tar/tar.c +++ b/src/vfs/tar/tar.c @@ -442,6 +442,9 @@ tar_fill_stat (struct vfs_s_super *archive, struct stat *st, union record *heade } st->st_size = h_size; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + st->st_atim.tv_nsec = st->st_mtim.tv_nsec = st->st_ctim.tv_nsec = 0; +#endif st->st_mtime = tar_from_oct (1 + 12, header->header.mtime); st->st_atime = 0; st->st_ctime = 0; From f0414be51dab00b8bcb2d4aa9f18d11754a5e095 Mon Sep 17 00:00:00 2001 From: "Yury V. Zaytsev" Date: Sat, 6 May 2017 20:57:59 +0200 Subject: [PATCH 2/2] Zero struct stat tv_nsec (if supported) whenever st is filled manually Signed-off-by: Yury V. Zaytsev --- lib/vfs/direntry.c | 4 ++++ lib/vfs/parse_ls_vga.c | 4 ++++ src/vfs/extfs/extfs.c | 3 +++ src/vfs/fish/fish.c | 6 ++++++ src/vfs/sftpfs/file.c | 4 ++++ src/vfs/sftpfs/internal.c | 6 ++++++ src/vfs/undelfs/undelfs.c | 3 +++ tests/lib/vfs/vfs_parse_ls_lga.c | 12 ++++++++++++ 8 files changed, 42 insertions(+) diff --git a/lib/vfs/direntry.c b/lib/vfs/direntry.c index 99ee6c91c..41b42ee3b 100644 --- a/lib/vfs/direntry.c +++ b/lib/vfs/direntry.c @@ -976,7 +976,11 @@ vfs_s_default_stat (struct vfs_class *me, mode_t mode) st.st_blksize = 512; #endif st.st_size = 0; + st.st_mtime = st.st_atime = st.st_ctime = time (NULL); +#ifdef HAVE_STRUCT_STAT_ST_MTIM + st.st_atim.tv_nsec = st.st_mtim.tv_nsec = st.st_ctim.tv_nsec = 0; +#endif vfs_adjust_stat (&st); diff --git a/lib/vfs/parse_ls_vga.c b/lib/vfs/parse_ls_vga.c index ee214a06f..be8d972c8 100644 --- a/lib/vfs/parse_ls_vga.c +++ b/lib/vfs/parse_ls_vga.c @@ -809,6 +809,10 @@ vfs_parse_ls_lga (const char *p, struct stat * s, char **filename, char **linkna goto error; /* Use resulting time value */ s->st_atime = s->st_ctime = s->st_mtime; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + s->st_atim.tv_nsec = s->st_mtim.tv_nsec = s->st_ctim.tv_nsec = 0; +#endif + /* s->st_dev and s->st_ino must be initialized by vfs_s_new_inode () */ #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE s->st_blksize = 512; diff --git a/src/vfs/extfs/extfs.c b/src/vfs/extfs/extfs.c index ee56f9597..82c42593a 100644 --- a/src/vfs/extfs/extfs.c +++ b/src/vfs/extfs/extfs.c @@ -1099,6 +1099,9 @@ extfs_stat_move (struct stat *buf, const struct inode *inode) buf->st_atime = inode->atime; buf->st_mtime = inode->mtime; buf->st_ctime = inode->ctime; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + buf->st_atim.tv_nsec = buf->st_mtim.tv_nsec = buf->st_ctim.tv_nsec = 0; +#endif } /* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/fish/fish.c b/src/vfs/fish/fish.c index fc74af391..12ca2f272 100644 --- a/src/vfs/fish/fish.c +++ b/src/vfs/fish/fish.c @@ -834,6 +834,9 @@ fish_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path) if (vfs_parse_filedate (0, &ST.st_ctime) == 0) break; ST.st_atime = ST.st_mtime = ST.st_ctime; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + ST.st_atim.tv_nsec = ST.st_mtim.tv_nsec = ST.st_ctim.tv_nsec = 0; +#endif } break; case 'D': @@ -845,6 +848,9 @@ fish_dir_load (struct vfs_class *me, struct vfs_s_inode *dir, char *remote_path) &tim.tm_mday, &tim.tm_hour, &tim.tm_min, &tim.tm_sec) != 6) break; ST.st_atime = ST.st_mtime = ST.st_ctime = mktime (&tim); +#ifdef HAVE_STRUCT_STAT_ST_MTIM + ST.st_atim.tv_nsec = ST.st_mtim.tv_nsec = ST.st_ctim.tv_nsec = 0; +#endif } break; case 'E': diff --git a/src/vfs/sftpfs/file.c b/src/vfs/sftpfs/file.c index cf61c9b26..3a0c625df 100644 --- a/src/vfs/sftpfs/file.c +++ b/src/vfs/sftpfs/file.c @@ -230,6 +230,10 @@ sftpfs_fstat (void *data, struct stat *buf, GError ** mcerror) buf->st_atime = attrs.atime; buf->st_mtime = attrs.mtime; buf->st_ctime = attrs.mtime; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + buf->st_atim.tv_nsec = buf->st_mtim.tv_nsec = buf->st_ctim.tv_nsec = 0; +#endif + } if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) != 0) diff --git a/src/vfs/sftpfs/internal.c b/src/vfs/sftpfs/internal.c index e33af813b..4daf91da9 100644 --- a/src/vfs/sftpfs/internal.c +++ b/src/vfs/sftpfs/internal.c @@ -203,6 +203,9 @@ sftpfs_lstat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror) buf->st_atime = attrs.atime; buf->st_mtime = attrs.mtime; buf->st_ctime = attrs.mtime; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + buf->st_atim.tv_nsec = buf->st_mtim.tv_nsec = buf->st_ctim.tv_nsec = 0; +#endif } if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) != 0) @@ -286,6 +289,9 @@ sftpfs_stat (const vfs_path_t * vpath, struct stat *buf, GError ** mcerror) buf->st_atime = attrs.atime; buf->st_mtime = attrs.mtime; buf->st_ctime = attrs.mtime; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + buf->st_atim.tv_nsec = buf->st_mtim.tv_nsec = buf->st_ctim.tv_nsec = 0; +#endif } if ((attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) != 0) diff --git a/src/vfs/undelfs/undelfs.c b/src/vfs/undelfs/undelfs.c index 88769f92f..0719911c7 100644 --- a/src/vfs/undelfs/undelfs.c +++ b/src/vfs/undelfs/undelfs.c @@ -634,6 +634,9 @@ undelfs_stat_int (int inode_index, struct stat *buf) buf->st_atime = delarray[inode_index].dtime; buf->st_ctime = delarray[inode_index].dtime; buf->st_mtime = delarray[inode_index].dtime; +#ifdef HAVE_STRUCT_STAT_ST_MTIM + buf->st_atim.tv_nsec = buf->st_mtim.tv_nsec = buf->st_ctim.tv_nsec = 0; +#endif return 0; } diff --git a/tests/lib/vfs/vfs_parse_ls_lga.c b/tests/lib/vfs/vfs_parse_ls_lga.c index e669f92e7..cb455eaf6 100644 --- a/tests/lib/vfs/vfs_parse_ls_lga.c +++ b/tests/lib/vfs/vfs_parse_ls_lga.c @@ -110,6 +110,11 @@ message (int flags, const char *title, const char *text, ...) static void fill_stat_struct (struct stat *etalon_stat, int iterator) { + +#ifdef HAVE_STRUCT_STAT_ST_MTIM + etalon_stat->st_atim.tv_nsec = etalon_stat->st_mtim.tv_nsec = etalon_stat->st_ctim.tv_nsec = 0; +#endif + switch (iterator) { case 0: @@ -299,6 +304,13 @@ START_PARAMETRIZED_TEST (test_vfs_parse_ls_lga, test_vfs_parse_ls_lga_ds) mctest_assert_int_eq (etalon_stat.st_mtime, test_stat.st_mtime); mctest_assert_int_eq (etalon_stat.st_ctime, test_stat.st_ctime); */ + +#ifdef HAVE_STRUCT_STAT_ST_MTIM + mctest_assert_int_eq (0, test_stat.st_atim.tv_nsec); + mctest_assert_int_eq (0, test_stat.st_mtim.tv_nsec); + mctest_assert_int_eq (0, test_stat.st_ctim.tv_nsec); +#endif + } /* *INDENT-OFF* */ END_PARAMETRIZED_TEST