From 4c418047b2f3731809b4146c367b68ec030ba4f2 Mon Sep 17 00:00:00 2001 From: Andrey Gursky Date: Mon, 7 Dec 2015 00:39:05 +0100 Subject: [PATCH 1/3] Ticket #3575: preserve timestamps with nanosecond precisions during file copy MC truncates timestamps during file copy and drops sub-second precision. Make use of utimensat(), introduced in Linux kernel 2.6.22 (and since 2.6.26 compatible with POSIX-1.2008). Signed-off-by: Andrey Gursky Signed-off-by: Andrew Borodin --- configure.ac | 3 +++ lib/vfs/interface.c | 2 +- lib/vfs/vfs.h | 14 ++++++++--- src/filemanager/file.c | 28 +++++++++++++++------- src/vfs/fish/fish.c | 49 +++++++++++++++++++++++++++++++------- src/vfs/local/local.c | 10 ++++++-- src/vfs/sfs/sfs.c | 11 +++++++-- src/vfs/sftpfs/vfs_class.c | 2 +- src/vfs/smbfs/smbfs.c | 6 ++++- 9 files changed, 99 insertions(+), 26 deletions(-) diff --git a/configure.ac b/configure.ac index c9155a3ef..38776bfdb 100644 --- a/configure.ac +++ b/configure.ac @@ -229,6 +229,9 @@ AC_CHECK_FUNCS([\ realpath ]) +dnl utimensat is supported since glibc 2.6 and specified in POSIX.1-2008 +AC_CHECK_FUNCS([utimensat]) + dnl getpt is a GNU Extension (glibc 2.1.x) AC_CHECK_FUNCS(posix_openpt, , [AC_CHECK_FUNCS(getpt)]) AC_CHECK_FUNCS(grantpt, , [AC_CHECK_LIB(pt, grantpt)]) diff --git a/lib/vfs/interface.c b/lib/vfs/interface.c index 590b1ce3a..1334eb1a3 100644 --- a/lib/vfs/interface.c +++ b/lib/vfs/interface.c @@ -251,7 +251,7 @@ int mc_##name inarg \ MC_NAMEOP (chmod, (const vfs_path_t *vpath, mode_t mode), (vpath, mode)) MC_NAMEOP (chown, (const vfs_path_t *vpath, uid_t owner, gid_t group), (vpath, owner, group)) -MC_NAMEOP (utime, (const vfs_path_t *vpath, struct utimbuf * times), (vpath, times)) +MC_NAMEOP (utime, (const vfs_path_t *vpath, mc_timesbuf_t * times), (vpath, times)) MC_NAMEOP (readlink, (const vfs_path_t *vpath, char *buf, size_t bufsiz), (vpath, buf, bufsiz)) MC_NAMEOP (unlink, (const vfs_path_t *vpath), (vpath)) MC_NAMEOP (mkdir, (const vfs_path_t *vpath, mode_t mode), (vpath, mode)) diff --git a/lib/vfs/vfs.h b/lib/vfs/vfs.h index 41f7724d1..a4d4c8dd1 100644 --- a/lib/vfs/vfs.h +++ b/lib/vfs/vfs.h @@ -10,7 +10,9 @@ #include #include #include -#ifdef HAVE_UTIME_H +#ifdef HAVE_UTIMENSAT +#include +#elif defined (HAVE_UTIME_H) #include #endif #include @@ -97,6 +99,12 @@ typedef void (*fill_names_f) (const char *); typedef void *vfsid; +#ifdef HAVE_UTIMENSAT +typedef struct timespec mc_timesbuf_t[2]; +#else +typedef struct utimbuf mc_timesbuf_t; +#endif + /*** enums ***************************************************************************************/ /* Flags of VFS classes */ @@ -167,7 +175,7 @@ typedef struct vfs_class int (*chmod) (const vfs_path_t * vpath, mode_t mode); int (*chown) (const vfs_path_t * vpath, uid_t owner, gid_t group); - int (*utime) (const vfs_path_t * vpath, struct utimbuf * times); + int (*utime) (const vfs_path_t * vpath, mc_timesbuf_t * times); int (*readlink) (const vfs_path_t * vpath, char *buf, size_t size); int (*symlink) (const vfs_path_t * vpath1, const vfs_path_t * vpath2); @@ -279,7 +287,7 @@ int vfs_preallocate (int dest_desc, off_t src_fsize, off_t dest_fsize); */ ssize_t mc_read (int handle, void *buffer, size_t count); ssize_t mc_write (int handle, const void *buffer, size_t count); -int mc_utime (const vfs_path_t * vpath, struct utimbuf *times); +int mc_utime (const vfs_path_t * vpath, mc_timesbuf_t * times); int mc_readlink (const vfs_path_t * vpath, char *buf, size_t bufsiz); int mc_close (int handle); off_t mc_lseek (int fd, off_t offset, int whence); diff --git a/src/filemanager/file.c b/src/filemanager/file.c index ff97997c4..e4e08bd46 100644 --- a/src/filemanager/file.c +++ b/src/filemanager/file.c @@ -659,6 +659,20 @@ warn_same_file (const char *fmt, const char *a, const char *b) return real_warn_same_file (Foreground, fmt, a, b); } +/* --------------------------------------------------------------------------------------------- */ + +static void +get_times (const struct stat *sb, mc_timesbuf_t * times) +{ +#ifdef HAVE_UTIMENSAT + (*times)[0] = sb->st_atim; + (*times)[1] = sb->st_mtim; +#else + times->actime = sb->st_atime; + times->modtime = sb->st_mtime; +#endif +} + /* --------------------------------------------------------------------------------------------- */ /* {{{ Query/status report routines */ @@ -1484,7 +1498,7 @@ copy_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx, int src_desc, dest_desc = -1; mode_t src_mode = 0; /* The mode of the source file */ struct stat src_stat, dst_stat; - struct utimbuf utb; + mc_timesbuf_t times; gboolean dst_exists = FALSE, appending = FALSE; off_t file_size = -1; FileProgressStatus return_status, temp_status; @@ -1679,8 +1693,7 @@ copy_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx, src_mode = src_stat.st_mode; src_uid = src_stat.st_uid; src_gid = src_stat.st_gid; - utb.actime = src_stat.st_atime; - utb.modtime = src_stat.st_mtime; + get_times (&src_stat, ×); file_size = src_stat.st_size; open_flags = O_WRONLY; @@ -1995,7 +2008,7 @@ copy_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx, src_mode = 0100666 & ~src_mode; mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)); } - mc_utime (dst_vpath, &utb); + mc_utime (dst_vpath, ×); } } @@ -2257,12 +2270,11 @@ copy_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const cha if (ctx->preserve) { - struct utimbuf utb; + mc_timesbuf_t times; mc_chmod (dst_vpath, cbuf.st_mode & ctx->umask_kill); - utb.actime = cbuf.st_atime; - utb.modtime = cbuf.st_mtime; - mc_utime (dst_vpath, &utb); + get_times (&cbuf, ×); + mc_utime (dst_vpath, ×); } else { diff --git a/src/vfs/fish/fish.c b/src/vfs/fish/fish.c index d4e06fa1f..e04c87730 100644 --- a/src/vfs/fish/fish.c +++ b/src/vfs/fish/fish.c @@ -1323,11 +1323,42 @@ fish_chown (const vfs_path_t * vpath, uid_t owner, gid_t group) /* --------------------------------------------------------------------------------------------- */ +static time_t +fish_get_atime (mc_timesbuf_t * times) +{ + time_t ret; + +#ifdef HAVE_UTIMENSAT + ret = (*times)[0].tv_sec; +#else + ret = times->actime; +#endif + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + +static time_t +fish_get_mtime (mc_timesbuf_t * times) +{ + time_t ret; + +#ifdef HAVE_UTIMENSAT + ret = (*times)[1].tv_sec; +#else + ret = times->modtime; +#endif + return ret; +} + +/* --------------------------------------------------------------------------------------------- */ + static int -fish_utime (const vfs_path_t * vpath, struct utimbuf *times) +fish_utime (const vfs_path_t * vpath, mc_timesbuf_t * times) { gchar *shell_commands = NULL; - char utcmtime[16], utcatime[16]; + char utcatime[16], utcmtime[16]; + time_t atime, mtime; struct tm *gmt; char buf[BUF_LARGE]; const char *crpath; @@ -1342,21 +1373,23 @@ fish_utime (const vfs_path_t * vpath, struct utimbuf *times) return -1; rpath = strutils_shell_escape (crpath); - gmt = gmtime (×->modtime); - g_snprintf (utcmtime, sizeof (utcmtime), "%04d%02d%02d%02d%02d.%02d", + atime = fish_get_atime (times); + gmt = gmtime (&atime); + g_snprintf (utcatime, sizeof (utcatime), "%04d%02d%02d%02d%02d.%02d", gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, gmt->tm_hour, gmt->tm_min, gmt->tm_sec); - gmt = gmtime (×->actime); - g_snprintf (utcatime, sizeof (utcatime), "%04d%02d%02d%02d%02d.%02d", + mtime = fish_get_mtime (times); + gmt = gmtime (&mtime); + g_snprintf (utcmtime, sizeof (utcmtime), "%04d%02d%02d%02d%02d.%02d", gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, gmt->tm_hour, gmt->tm_min, gmt->tm_sec); shell_commands = g_strconcat (SUP->scr_env, "FISH_FILENAME=%s FISH_FILEATIME=%ld FISH_FILEMTIME=%ld ", "FISH_TOUCHATIME=%s FISH_TOUCHMTIME=%s;\n", SUP->scr_utime, (char *) NULL); - g_snprintf (buf, sizeof (buf), shell_commands, rpath, (long) times->actime, - (long) times->modtime, utcatime, utcmtime); + g_snprintf (buf, sizeof (buf), shell_commands, rpath, (long) atime, (long) mtime, + utcatime, utcmtime); g_free (shell_commands); g_free (rpath); return fish_send_command (path_element->class, super, buf, OPT_FLUSH); diff --git a/src/vfs/local/local.c b/src/vfs/local/local.c index 2112e1006..9ae467717 100644 --- a/src/vfs/local/local.c +++ b/src/vfs/local/local.c @@ -169,12 +169,18 @@ local_chown (const vfs_path_t * vpath, uid_t owner, gid_t group) /* --------------------------------------------------------------------------------------------- */ static int -local_utime (const vfs_path_t * vpath, struct utimbuf *times) +local_utime (const vfs_path_t * vpath, mc_timesbuf_t * times) { + int ret; const vfs_path_element_t *path_element; path_element = vfs_path_get_by_index (vpath, -1); - return utime (path_element->path, times); +#ifdef HAVE_UTIMENSAT + ret = utimensat (AT_FDCWD, path_element->path, *times, 0); +#else + ret = utime (path_element->path, times); +#endif + return ret; } /* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sfs/sfs.c b/src/vfs/sfs/sfs.c index f2b65cc88..2a07b00d8 100644 --- a/src/vfs/sfs/sfs.c +++ b/src/vfs/sfs/sfs.c @@ -319,9 +319,16 @@ sfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group) /* --------------------------------------------------------------------------------------------- */ static int -sfs_utime (const vfs_path_t * vpath, struct utimbuf *times) +sfs_utime (const vfs_path_t * vpath, mc_timesbuf_t * times) { - return utime (sfs_redirect (vpath), times); + int ret; + +#ifdef HAVE_UTIMENSAT + ret = utimensat (AT_FDCWD, sfs_redirect (vpath), *times, 0); +#else + ret = utime (sfs_redirect (vpath), times); +#endif + return ret; } /* --------------------------------------------------------------------------------------------- */ diff --git a/src/vfs/sftpfs/vfs_class.c b/src/vfs/sftpfs/vfs_class.c index 189678fa2..46e2f7fc1 100644 --- a/src/vfs/sftpfs/vfs_class.c +++ b/src/vfs/sftpfs/vfs_class.c @@ -330,7 +330,7 @@ sftpfs_cb_readlink (const vfs_path_t * vpath, char *buf, size_t size) */ static int -sftpfs_cb_utime (const vfs_path_t * vpath, struct utimbuf *times) +sftpfs_cb_utime (const vfs_path_t * vpath, mc_timesbuf_t * times) { (void) vpath; (void) times; diff --git a/src/vfs/smbfs/smbfs.c b/src/vfs/smbfs/smbfs.c index ebf898865..73a25f274 100644 --- a/src/vfs/smbfs/smbfs.c +++ b/src/vfs/smbfs/smbfs.c @@ -995,14 +995,18 @@ smbfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group) /* --------------------------------------------------------------------------------------------- */ static int -smbfs_utime (const vfs_path_t * vpath, struct utimbuf *times) +smbfs_utime (const vfs_path_t * vpath, mc_timesbuf_t * times) { const vfs_path_element_t *path_element; (void) times; path_element = vfs_path_get_by_index (vpath, -1); +#ifdef HAVE_UTIMENSAT + DEBUG (3, ("smbfs_utimensat(path:%s)\n", path_element->path)); +#else DEBUG (3, ("smbfs_utime(path:%s)\n", path_element->path)); +#endif my_errno = EOPNOTSUPP; return -1; } From 53a1d52ed9508d507f526e16921aa86fa832d8e0 Mon Sep 17 00:00:00 2001 From: Andrew Borodin Date: Thu, 17 Nov 2016 14:29:53 +0300 Subject: [PATCH 2/3] (fish_utime): avoid buffer overflow: allocate buffer for command dynamically. Signed-off-by: Andrew Borodin --- src/vfs/fish/fish.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vfs/fish/fish.c b/src/vfs/fish/fish.c index e04c87730..9cbac242d 100644 --- a/src/vfs/fish/fish.c +++ b/src/vfs/fish/fish.c @@ -1360,11 +1360,12 @@ fish_utime (const vfs_path_t * vpath, mc_timesbuf_t * times) char utcatime[16], utcmtime[16]; time_t atime, mtime; struct tm *gmt; - char buf[BUF_LARGE]; + char *cmd; const char *crpath; char *rpath; struct vfs_s_super *super; const vfs_path_element_t *path_element; + int ret; path_element = vfs_path_get_by_index (vpath, -1); @@ -1388,11 +1389,13 @@ fish_utime (const vfs_path_t * vpath, mc_timesbuf_t * times) shell_commands = g_strconcat (SUP->scr_env, "FISH_FILENAME=%s FISH_FILEATIME=%ld FISH_FILEMTIME=%ld ", "FISH_TOUCHATIME=%s FISH_TOUCHMTIME=%s;\n", SUP->scr_utime, (char *) NULL); - g_snprintf (buf, sizeof (buf), shell_commands, rpath, (long) atime, (long) mtime, - utcatime, utcmtime); + cmd = g_strdup_printf (shell_commands, rpath, (long) atime, (long) mtime, utcatime, utcmtime); g_free (shell_commands); g_free (rpath); - return fish_send_command (path_element->class, super, buf, OPT_FLUSH); + ret = fish_send_command (path_element->class, super, cmd, OPT_FLUSH); + g_free (cmd); + + return ret; } /* --------------------------------------------------------------------------------------------- */ From 0075944f1eb31223688f60a9a127a8c1ec27c885 Mon Sep 17 00:00:00 2001 From: Andrey Gursky Date: Sat, 29 Oct 2016 02:31:40 +0200 Subject: [PATCH 3/3] FISH VFS: generate timestamps with nanosecond precision for touch. Signed-off-by: Andrew Borodin --- src/vfs/fish/fish.c | 46 +++++++++++++++++++++++--------------- src/vfs/fish/fishdef.h | 10 ++++++--- src/vfs/fish/helpers/utime | 11 +++++---- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/src/vfs/fish/fish.c b/src/vfs/fish/fish.c index 9cbac242d..4125ab635 100644 --- a/src/vfs/fish/fish.c +++ b/src/vfs/fish/fish.c @@ -1323,32 +1323,30 @@ fish_chown (const vfs_path_t * vpath, uid_t owner, gid_t group) /* --------------------------------------------------------------------------------------------- */ -static time_t -fish_get_atime (mc_timesbuf_t * times) +static void +fish_get_atime (mc_timesbuf_t * times, time_t * sec, long *nsec) { - time_t ret; - #ifdef HAVE_UTIMENSAT - ret = (*times)[0].tv_sec; + *sec = (*times)[0].tv_sec; + *nsec = (*times)[0].tv_nsec; #else - ret = times->actime; + *sec = times->actime; + *nsec = 0; #endif - return ret; } /* --------------------------------------------------------------------------------------------- */ -static time_t -fish_get_mtime (mc_timesbuf_t * times) +static void +fish_get_mtime (mc_timesbuf_t * times, time_t * sec, long *nsec) { - time_t ret; - #ifdef HAVE_UTIMENSAT - ret = (*times)[1].tv_sec; + *sec = (*times)[1].tv_sec; + *nsec = (*times)[1].tv_nsec; #else - ret = times->modtime; + *sec = times->modtime; + *nsec = 0; #endif - return ret; } /* --------------------------------------------------------------------------------------------- */ @@ -1358,7 +1356,9 @@ fish_utime (const vfs_path_t * vpath, mc_timesbuf_t * times) { gchar *shell_commands = NULL; char utcatime[16], utcmtime[16]; + char utcatime_w_nsec[30], utcmtime_w_nsec[30]; time_t atime, mtime; + long atime_nsec, mtime_nsec; struct tm *gmt; char *cmd; const char *crpath; @@ -1374,22 +1374,32 @@ fish_utime (const vfs_path_t * vpath, mc_timesbuf_t * times) return -1; rpath = strutils_shell_escape (crpath); - atime = fish_get_atime (times); + fish_get_atime (times, &atime, &atime_nsec); gmt = gmtime (&atime); g_snprintf (utcatime, sizeof (utcatime), "%04d%02d%02d%02d%02d.%02d", gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, gmt->tm_hour, gmt->tm_min, gmt->tm_sec); + g_snprintf (utcatime_w_nsec, sizeof (utcatime_w_nsec), "%04d-%02d-%02d %02d:%02d:%02d.%09ld", + gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, + gmt->tm_hour, gmt->tm_min, gmt->tm_sec, atime_nsec); - mtime = fish_get_mtime (times); + fish_get_mtime (times, &mtime, &mtime_nsec); gmt = gmtime (&mtime); g_snprintf (utcmtime, sizeof (utcmtime), "%04d%02d%02d%02d%02d.%02d", gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, gmt->tm_hour, gmt->tm_min, gmt->tm_sec); + g_snprintf (utcmtime_w_nsec, sizeof (utcmtime_w_nsec), "%04d-%02d-%02d %02d:%02d:%02d.%09ld", + gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, + gmt->tm_hour, gmt->tm_min, gmt->tm_sec, mtime_nsec); shell_commands = g_strconcat (SUP->scr_env, "FISH_FILENAME=%s FISH_FILEATIME=%ld FISH_FILEMTIME=%ld ", - "FISH_TOUCHATIME=%s FISH_TOUCHMTIME=%s;\n", SUP->scr_utime, (char *) NULL); - cmd = g_strdup_printf (shell_commands, rpath, (long) atime, (long) mtime, utcatime, utcmtime); + "FISH_TOUCHATIME=%s FISH_TOUCHMTIME=%s ", + "FISH_TOUCHATIME_W_NSEC=\"%s\" FISH_TOUCHMTIME_W_NSEC=\"%s\";\n", + SUP->scr_utime, (char *) NULL); + cmd = + g_strdup_printf (shell_commands, rpath, (long) atime, (long) mtime, utcatime, utcmtime, + utcatime_w_nsec, utcmtime_w_nsec); g_free (shell_commands); g_free (rpath); ret = fish_send_command (path_element->class, super, cmd, OPT_FLUSH); diff --git a/src/vfs/fish/fishdef.h b/src/vfs/fish/fishdef.h index c9761e363..1bb836bb3 100644 --- a/src/vfs/fish/fishdef.h +++ b/src/vfs/fish/fishdef.h @@ -76,17 +76,21 @@ /* default 'utime' script */ #define FISH_UTIME_DEF_CONTENT "" \ -"#UTIME $FISH_FILEATIME $FISH_FILEMTIME $FISH_FILENAME\n" \ -"if [ -n \"$FISH_HAVE_PERL\" ] && \\\n" \ -" perl -e 'utime '$FISH_FILEATIME','$FISH_FILEMTIME',@ARGV;' \"/${FISH_FILENAME}\" 2>/dev/null; then\n" \ +"#UTIME \"$FISH_TOUCHATIME_W_NSEC\" \"$FISH_TOUCHMTIME_W_NSEC\" $FISH_FILENAME\n" \ +"if TZ=UTC touch -m -d \"$FISH_TOUCHMTIME_W_NSEC\" \"/${FISH_FILENAME}\" 2>/dev/null && \\\n" \ +" TZ=UTC touch -a -d \"$FISH_TOUCHATIME_W_NSEC\" \"/${FISH_FILENAME}\" 2>/dev/null; then\n" \ " echo \"### 000\"\n" \ "elif TZ=UTC touch -m -t $FISH_TOUCHMTIME \"/${FISH_FILENAME}\" 2>/dev/null && \\\n" \ " TZ=UTC touch -a -t $FISH_TOUCHATIME \"/${FISH_FILENAME}\" 2>/dev/null; then\n" \ " echo \"### 000\"\n" \ +"elif [ -n \"$FISH_HAVE_PERL\" ] && \\\n" \ +" perl -e 'utime '$FISH_FILEATIME','$FISH_FILEMTIME',@ARGV;' \"/${FISH_FILENAME}\" 2>/dev/null; then\n" \ +" echo \"### 000\"\n" \ "else\n" \ " echo \"### 500\"\n" \ "fi\n" + /* default 'rmdir' script */ #define FISH_RMDIR_DEF_CONTENT "" \ "#RMD $FISH_FILENAME\n" \ diff --git a/src/vfs/fish/helpers/utime b/src/vfs/fish/helpers/utime index 0e4d82eae..bdd7a6089 100644 --- a/src/vfs/fish/helpers/utime +++ b/src/vfs/fish/helpers/utime @@ -1,10 +1,13 @@ -#UTIME $FISH_FILEATIME $FISH_FILEMTIME $FISH_FILENAME -if [ -n "$FISH_HAVE_PERL" ] && - perl -e 'utime '$FISH_FILEATIME','$FISH_FILEMTIME',@ARGV;' "/${FISH_FILENAME}" 2>/dev/null; then +#UTIME "$FISH_TOUCHATIME_W_NSEC" "$FISH_TOUCHMTIME_W_NSEC" "$FISH_FILENAME" +if TZ=UTC touch -m -d "$FISH_TOUCHMTIME_W_NSEC" "/${FISH_FILENAME}" 2>/dev/null && \ + TZ=UTC touch -a -d "$FISH_TOUCHATIME_W_NSEC" "/${FISH_FILENAME}" 2>/dev/null; then echo "### 000" -elif TZ=UTC touch -m -t $FISH_TOUCHMTIME "/${FISH_FILENAME}" 2>/dev/null && +elif TZ=UTC touch -m -t $FISH_TOUCHMTIME "/${FISH_FILENAME}" 2>/dev/null && \ TZ=UTC touch -a -t $FISH_TOUCHATIME "/${FISH_FILENAME}" 2>/dev/null; then echo "### 000" +elif [ -n "$FISH_HAVE_PERL" ] && + perl -e 'utime '$FISH_FILEATIME','$FISH_FILEMTIME',@ARGV;' "/${FISH_FILENAME}" 2>/dev/null; then + echo "### 000" else echo "### 500" fi