From ce8d8195dc253a87cceaaeeb1a725090471ae4f8 Mon Sep 17 00:00:00 2001 From: Niklas Hambüchen Date: Sat, 18 Feb 2017 00:49:02 +0100 Subject: posix: use nanosecond accuracy when available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Programs that set mtime, such as `rsync -a`, don't work correctly on GlusterFS, because it sets the nanoseconds to 000. This creates problems for incremental backups, where files get accidentally copied again and again. For example, consider `myfile` on an ext4 system, being copied to a GlusterFS volume, with `rsync -a` and then `cp -u` in turn. You'd expect that after the first `rsync -a`, `cp -u` agrees that the file need not be copied. BUG: 1422074 Change-Id: I89c7b6a73e2e06c02851ff76b7e5cdfaa271e985 Signed-off-by: Niklas Hambüchen Reviewed-on: https://review.gluster.org/16667 Smoke: Gluster Build System Reviewed-by: Niels de Vos Tested-by: Jeff Darcy NetBSD-regression: NetBSD Build System Reviewed-by: jiffin tony Thottan CentOS-regression: Gluster Build System Reviewed-by: Jeff Darcy --- libglusterfs/src/common-utils.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'libglusterfs/src/common-utils.c') diff --git a/libglusterfs/src/common-utils.c b/libglusterfs/src/common-utils.c index 51f97442fec..e18c97f5aa1 100644 --- a/libglusterfs/src/common-utils.c +++ b/libglusterfs/src/common-utils.c @@ -3806,7 +3806,11 @@ int gf_set_timestamp (const char *src, const char* dest) { struct stat sb = {0, }; - struct timeval new_time[2] = {{0, },{0,}}; +#if defined(HAVE_UTIMENSAT) + struct timespec new_time[2] = { {0, }, {0, } }; +#else + struct timeval new_time[2] = { {0, }, {0, } }; +#endif int ret = 0; xlator_t *this = NULL; @@ -3821,21 +3825,35 @@ gf_set_timestamp (const char *src, const char* dest) LG_MSG_FILE_STAT_FAILED, "stat on %s", src); goto out; } + /* The granularity is nano seconds if `utimensat()` is available, + * and micro seconds otherwise. + */ +#if defined(HAVE_UTIMENSAT) + new_time[0].tv_sec = sb.st_atime; + new_time[0].tv_nsec = ST_ATIM_NSEC (&sb); + + new_time[1].tv_sec = sb.st_mtime; + new_time[1].tv_nsec = ST_MTIM_NSEC (&sb); + + /* dirfd = 0 is ignored because `dest` is an absolute path. */ + ret = sys_utimensat (AT_FDCWD, dest, new_time, AT_SYMLINK_NOFOLLOW); + if (ret) { + gf_msg (this->name, GF_LOG_ERROR, errno, + LG_MSG_UTIMENSAT_FAILED, "utimensat on %s", dest); + } +#else new_time[0].tv_sec = sb.st_atime; new_time[0].tv_usec = ST_ATIM_NSEC (&sb)/1000; new_time[1].tv_sec = sb.st_mtime; new_time[1].tv_usec = ST_MTIM_NSEC (&sb)/1000; - /* The granularity is micro seconds as per the current - * requiremnt. Hence using 'utimes'. This can be updated - * to 'utimensat' if we need timestamp in nanoseconds. - */ ret = sys_utimes (dest, new_time); if (ret) { gf_msg (this->name, GF_LOG_ERROR, errno, LG_MSG_UTIMES_FAILED, "utimes on %s", dest); } +#endif out: return ret; } -- cgit