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 <andrey.gursky@e-mail.ua>
Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
This commit is contained in:
Andrey Gursky 2015-12-07 00:39:05 +01:00 committed by Andrew Borodin
parent 9f47eecc91
commit 4c418047b2
9 changed files with 99 additions and 26 deletions

View File

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

View File

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

View File

@ -10,7 +10,9 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#ifdef HAVE_UTIME_H
#ifdef HAVE_UTIMENSAT
#include <sys/time.h>
#elif defined (HAVE_UTIME_H)
#include <utime.h>
#endif
#include <stdio.h>
@ -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);

View File

@ -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, &times);
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, &times);
}
}
@ -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, &times);
mc_utime (dst_vpath, &times);
}
else
{

View File

@ -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 (&times->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 (&times->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);

View File

@ -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;
}
/* --------------------------------------------------------------------------------------------- */

View File

@ -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;
}
/* --------------------------------------------------------------------------------------------- */

View File

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

View File

@ -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;
}