From e6f9635425c94c37593858f0f15da4f1f54b3ba3 Mon Sep 17 00:00:00 2001
From: Andrey Gursky <andrey.gursky@e-mail.ua>
Date: Mon, 7 Dec 2015 00:39:05 +0100
Subject: [PATCH] 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>
---
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 91be803..a5a850f 100644
a
|
b
|
AC_CHECK_FUNCS([\ |
228 | 228 | realpath |
229 | 229 | ]) |
230 | 230 | |
| 231 | dnl utimensat is supported since glibc 2.6 and specified in POSIX.1-2008 |
| 232 | AC_CHECK_FUNCS([utimensat]) |
| 233 | |
231 | 234 | dnl getpt is a GNU Extension (glibc 2.1.x) |
232 | 235 | AC_CHECK_FUNCS(posix_openpt, , [AC_CHECK_FUNCS(getpt)]) |
233 | 236 | AC_CHECK_FUNCS(grantpt, , [AC_CHECK_LIB(pt, grantpt)]) |
diff --git a/lib/vfs/interface.c b/lib/vfs/interface.c
index 550fbef..74d3343 100644
a
|
b
|
int mc_##name inarg \ |
247 | 247 | |
248 | 248 | MC_NAMEOP (chmod, (const vfs_path_t *vpath, mode_t mode), (vpath, mode)) |
249 | 249 | MC_NAMEOP (chown, (const vfs_path_t *vpath, uid_t owner, gid_t group), (vpath, owner, group)) |
250 | | MC_NAMEOP (utime, (const vfs_path_t *vpath, struct utimbuf * times), (vpath, times)) |
| 250 | MC_NAMEOP (utime, (const vfs_path_t *vpath, const timesbuf_st *times), (vpath, times)) |
251 | 251 | MC_NAMEOP (readlink, (const vfs_path_t *vpath, char *buf, size_t bufsiz), (vpath, buf, bufsiz)) |
252 | 252 | MC_NAMEOP (unlink, (const vfs_path_t *vpath), (vpath)) |
253 | 253 | 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 ab6f7a2..ade1bb7 100644
a
|
b
|
|
10 | 10 | #include <sys/types.h> |
11 | 11 | #include <sys/stat.h> |
12 | 12 | #include <dirent.h> |
13 | | #ifdef HAVE_UTIME_H |
| 13 | #ifdef HAVE_UTIMENSAT |
| 14 | #include <sys/time.h> |
| 15 | #elif defined( HAVE_UTIME_H ) |
14 | 16 | #include <utime.h> |
15 | 17 | #endif |
16 | 18 | #include <stdio.h> |
… |
… |
typedef void (*fill_names_f) (const char *); |
97 | 99 | |
98 | 100 | typedef void *vfsid; |
99 | 101 | |
| 102 | #ifdef HAVE_UTIMENSAT |
| 103 | typedef struct timespec timesbuf_st[2]; |
| 104 | #else |
| 105 | typedef struct utimbuf timesbuf_st; |
| 106 | #endif |
| 107 | |
100 | 108 | /*** enums ***************************************************************************************/ |
101 | 109 | |
102 | 110 | /* Flags of VFS classes */ |
… |
… |
typedef struct vfs_class |
167 | 175 | |
168 | 176 | int (*chmod) (const vfs_path_t * vpath, mode_t mode); |
169 | 177 | int (*chown) (const vfs_path_t * vpath, uid_t owner, gid_t group); |
170 | | int (*utime) (const vfs_path_t * vpath, struct utimbuf * times); |
| 178 | int (*utime) (const vfs_path_t * vpath, const timesbuf_st *times); |
171 | 179 | |
172 | 180 | int (*readlink) (const vfs_path_t * vpath, char *buf, size_t size); |
173 | 181 | int (*symlink) (const vfs_path_t * vpath1, const vfs_path_t * vpath2); |
… |
… |
int vfs_preallocate (int dest_desc, off_t src_fsize, off_t dest_fsize); |
281 | 289 | */ |
282 | 290 | ssize_t mc_read (int handle, void *buffer, size_t count); |
283 | 291 | ssize_t mc_write (int handle, const void *buffer, size_t count); |
284 | | int mc_utime (const vfs_path_t * vpath, struct utimbuf *times); |
| 292 | int mc_utime (const vfs_path_t * vpath, const timesbuf_st *times); |
285 | 293 | int mc_readlink (const vfs_path_t * vpath, char *buf, size_t bufsiz); |
286 | 294 | int mc_close (int handle); |
287 | 295 | off_t mc_lseek (int fd, off_t offset, int whence); |
diff --git a/src/filemanager/file.c b/src/filemanager/file.c
index 0bacc54..aa6780f 100644
a
|
b
|
warn_same_file (const char *fmt, const char *a, const char *b) |
663 | 663 | } |
664 | 664 | |
665 | 665 | /* --------------------------------------------------------------------------------------------- */ |
| 666 | |
| 667 | static void |
| 668 | get_times( const struct stat *sb, timesbuf_st *times) |
| 669 | { |
| 670 | #ifdef HAVE_UTIMENSAT |
| 671 | (*times)[0] = sb->st_atim; |
| 672 | (*times)[1] = sb->st_mtim; |
| 673 | #else |
| 674 | times->actime = sb->st_atime; |
| 675 | times->modtime = sb->st_mtime; |
| 676 | #endif |
| 677 | } |
| 678 | |
| 679 | /* --------------------------------------------------------------------------------------------- */ |
666 | 680 | /* {{{ Query/status report routines */ |
667 | 681 | |
668 | 682 | static FileProgressStatus |
… |
… |
copy_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx, |
1487 | 1501 | int n_read, n_written; |
1488 | 1502 | mode_t src_mode = 0; /* The mode of the source file */ |
1489 | 1503 | struct stat sb, sb2; |
1490 | | struct utimbuf utb; |
| 1504 | timesbuf_st times; |
1491 | 1505 | gboolean dst_exists = FALSE, appending = FALSE; |
1492 | 1506 | off_t file_size = -1; |
1493 | 1507 | FileProgressStatus return_status, temp_status; |
… |
… |
copy_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx, |
1681 | 1695 | src_mode = sb.st_mode; |
1682 | 1696 | src_uid = sb.st_uid; |
1683 | 1697 | src_gid = sb.st_gid; |
1684 | | utb.actime = sb.st_atime; |
1685 | | utb.modtime = sb.st_mtime; |
| 1698 | get_times(&sb, ×); |
1686 | 1699 | file_size = sb.st_size; |
1687 | 1700 | |
1688 | 1701 | open_flags = O_WRONLY; |
… |
… |
copy_file_file (file_op_total_context_t * tctx, file_op_context_t * ctx, |
1992 | 2005 | src_mode = 0100666 & ~src_mode; |
1993 | 2006 | mc_chmod (dst_vpath, (src_mode & ctx->umask_kill)); |
1994 | 2007 | } |
1995 | | mc_utime (dst_vpath, &utb); |
| 2008 | mc_utime (dst_vpath, ×); |
1996 | 2009 | } |
1997 | 2010 | } |
1998 | 2011 | |
… |
… |
copy_dir_dir (file_op_total_context_t * tctx, file_op_context_t * ctx, const cha |
2254 | 2267 | |
2255 | 2268 | if (ctx->preserve) |
2256 | 2269 | { |
2257 | | struct utimbuf utb; |
| 2270 | timesbuf_st times; |
2258 | 2271 | |
2259 | 2272 | mc_chmod (dst_vpath, cbuf.st_mode & ctx->umask_kill); |
2260 | | utb.actime = cbuf.st_atime; |
2261 | | utb.modtime = cbuf.st_mtime; |
2262 | | mc_utime (dst_vpath, &utb); |
| 2273 | get_times(&cbuf, ×); |
| 2274 | mc_utime (dst_vpath, ×); |
2263 | 2275 | } |
2264 | 2276 | else |
2265 | 2277 | { |
diff --git a/src/vfs/fish/fish.c b/src/vfs/fish/fish.c
index 1172f31..35ec623 100644
a
|
b
|
fish_chown (const vfs_path_t * vpath, uid_t owner, gid_t group) |
1320 | 1320 | |
1321 | 1321 | /* --------------------------------------------------------------------------------------------- */ |
1322 | 1322 | |
| 1323 | static time_t |
| 1324 | get_atime(const timesbuf_st *times) |
| 1325 | { |
| 1326 | time_t ret; |
| 1327 | |
| 1328 | #ifdef HAVE_UTIMENSAT |
| 1329 | ret = (*times)[0].tv_sec; |
| 1330 | #else |
| 1331 | ret = times->actime; |
| 1332 | #endif |
| 1333 | return ret; |
| 1334 | } |
| 1335 | |
| 1336 | /* --------------------------------------------------------------------------------------------- */ |
| 1337 | |
| 1338 | static time_t |
| 1339 | get_mtime(const timesbuf_st *times) |
| 1340 | { |
| 1341 | time_t ret; |
| 1342 | |
| 1343 | #ifdef HAVE_UTIMENSAT |
| 1344 | ret = (*times)[1].tv_sec; |
| 1345 | #else |
| 1346 | ret = times->modtime; |
| 1347 | #endif |
| 1348 | return ret; |
| 1349 | } |
| 1350 | |
| 1351 | /* --------------------------------------------------------------------------------------------- */ |
| 1352 | |
1323 | 1353 | static int |
1324 | | fish_utime (const vfs_path_t * vpath, struct utimbuf *times) |
| 1354 | fish_utime (const vfs_path_t *vpath, const timesbuf_st *times) |
1325 | 1355 | { |
1326 | 1356 | gchar *shell_commands = NULL; |
1327 | | char utcmtime[16], utcatime[16]; |
| 1357 | char utcatime[16], utcmtime[16]; |
| 1358 | time_t atime, mtime; |
1328 | 1359 | struct tm *gmt; |
1329 | 1360 | |
1330 | 1361 | char buf[BUF_LARGE]; |
… |
… |
fish_utime (const vfs_path_t * vpath, struct utimbuf *times) |
1340 | 1371 | return -1; |
1341 | 1372 | rpath = strutils_shell_escape (crpath); |
1342 | 1373 | |
1343 | | gmt = gmtime (×->modtime); |
1344 | | g_snprintf (utcmtime, sizeof (utcmtime), "%04d%02d%02d%02d%02d.%02d", |
| 1374 | atime = get_atime(times); |
| 1375 | gmt = gmtime(&atime); |
| 1376 | g_snprintf (utcatime, sizeof (utcatime), "%04d%02d%02d%02d%02d.%02d", |
1345 | 1377 | gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, |
1346 | 1378 | gmt->tm_hour, gmt->tm_min, gmt->tm_sec); |
1347 | 1379 | |
1348 | | gmt = gmtime (×->actime); |
1349 | | g_snprintf (utcatime, sizeof (utcatime), "%04d%02d%02d%02d%02d.%02d", |
| 1380 | mtime = get_mtime(times); |
| 1381 | gmt = gmtime(&mtime); |
| 1382 | g_snprintf (utcmtime, sizeof (utcmtime), "%04d%02d%02d%02d%02d.%02d", |
1350 | 1383 | gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday, |
1351 | 1384 | gmt->tm_hour, gmt->tm_min, gmt->tm_sec); |
1352 | 1385 | |
1353 | 1386 | shell_commands = |
1354 | 1387 | g_strconcat (SUP->scr_env, "FISH_FILENAME=%s FISH_FILEATIME=%ld FISH_FILEMTIME=%ld ", |
1355 | 1388 | "FISH_TOUCHATIME=%s FISH_TOUCHMTIME=%s;\n", SUP->scr_utime, (char *) NULL); |
1356 | | g_snprintf (buf, sizeof (buf), shell_commands, rpath, (long) times->actime, |
1357 | | (long) times->modtime, utcatime, utcmtime); |
| 1389 | g_snprintf (buf, sizeof (buf), shell_commands, rpath, (long) atime, (long) mtime, |
| 1390 | utcatime, utcmtime); |
1358 | 1391 | g_free (shell_commands); |
1359 | 1392 | g_free (rpath); |
1360 | 1393 | 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 7ea70f2..75ed258 100644
a
|
b
|
local_chown (const vfs_path_t * vpath, uid_t owner, gid_t group) |
169 | 169 | /* --------------------------------------------------------------------------------------------- */ |
170 | 170 | |
171 | 171 | static int |
172 | | local_utime (const vfs_path_t * vpath, struct utimbuf *times) |
| 172 | local_utime (const vfs_path_t * vpath, const timesbuf_st *times) |
173 | 173 | { |
| 174 | int ret; |
174 | 175 | const vfs_path_element_t *path_element; |
175 | 176 | |
176 | 177 | path_element = vfs_path_get_by_index (vpath, -1); |
177 | | return utime (path_element->path, times); |
| 178 | #ifdef HAVE_UTIMENSAT |
| 179 | ret = utimensat (AT_FDCWD, path_element->path, *times, 0); |
| 180 | #else |
| 181 | ret = utime (path_element->path, times); |
| 182 | #endif |
| 183 | return ret; |
178 | 184 | } |
179 | 185 | |
180 | 186 | /* --------------------------------------------------------------------------------------------- */ |
diff --git a/src/vfs/sfs/sfs.c b/src/vfs/sfs/sfs.c
index 90b36ec..d6f554c 100644
a
|
b
|
sfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group) |
319 | 319 | /* --------------------------------------------------------------------------------------------- */ |
320 | 320 | |
321 | 321 | static int |
322 | | sfs_utime (const vfs_path_t * vpath, struct utimbuf *times) |
| 322 | sfs_utime (const vfs_path_t * vpath, const timesbuf_st *times) |
323 | 323 | { |
324 | | return utime (sfs_redirect (vpath), times); |
| 324 | int ret; |
| 325 | |
| 326 | #ifdef HAVE_UTIMENSAT |
| 327 | ret = utimensat (AT_FDCWD, sfs_redirect (vpath), *times, 0); |
| 328 | #else |
| 329 | ret = utime (sfs_redirect (vpath), times); |
| 330 | #endif |
| 331 | return ret; |
325 | 332 | } |
326 | 333 | |
327 | 334 | /* --------------------------------------------------------------------------------------------- */ |
diff --git a/src/vfs/sftpfs/vfs_class.c b/src/vfs/sftpfs/vfs_class.c
index 3504c26..d542cdd 100644
a
|
b
|
sftpfs_cb_readlink (const vfs_path_t * vpath, char *buf, size_t size) |
330 | 330 | */ |
331 | 331 | |
332 | 332 | static int |
333 | | sftpfs_cb_utime (const vfs_path_t * vpath, struct utimbuf *times) |
| 333 | sftpfs_cb_utime (const vfs_path_t * vpath, const timesbuf_st *times) |
334 | 334 | { |
335 | 335 | (void) vpath; |
336 | 336 | (void) times; |
diff --git a/src/vfs/smbfs/smbfs.c b/src/vfs/smbfs/smbfs.c
index cf507e6..bf1874f 100644
a
|
b
|
smbfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group) |
995 | 995 | /* --------------------------------------------------------------------------------------------- */ |
996 | 996 | |
997 | 997 | static int |
998 | | smbfs_utime (const vfs_path_t * vpath, struct utimbuf *times) |
| 998 | smbfs_utime (const vfs_path_t * vpath, const timesbuf_st *times) |
999 | 999 | { |
1000 | 1000 | const vfs_path_element_t *path_element; |
1001 | 1001 | |
1002 | 1002 | (void) times; |
1003 | 1003 | |
1004 | 1004 | path_element = vfs_path_get_by_index (vpath, -1); |
| 1005 | #ifdef HAVE_UTIMENSAT |
| 1006 | DEBUG (3, ("smbfs_utimensat(path:%s)\n", path_element->path)); |
| 1007 | #else |
1005 | 1008 | DEBUG (3, ("smbfs_utime(path:%s)\n", path_element->path)); |
| 1009 | #endif |
1006 | 1010 | my_errno = EOPNOTSUPP; |
1007 | 1011 | return -1; |
1008 | 1012 | } |