summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNiklas Hambüchen <mail@nh2.me>2017-02-18 00:49:02 +0100
committerJeff Darcy <jdarcy@redhat.com>2017-03-07 12:10:02 -0500
commitce8d8195dc253a87cceaaeeb1a725090471ae4f8 (patch)
tree87c027440d7a6af44956861b34fec3844d699038
parent89c6bedc1c2e978f67ca29f212a357984cd8a2dd (diff)
posix: use nanosecond accuracy when available
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 <mail@nh2.me> Reviewed-on: https://review.gluster.org/16667 Smoke: Gluster Build System <jenkins@build.gluster.org> Reviewed-by: Niels de Vos <ndevos@redhat.com> Tested-by: Jeff Darcy <jdarcy@redhat.com> NetBSD-regression: NetBSD Build System <jenkins@build.gluster.org> Reviewed-by: jiffin tony Thottan <jthottan@redhat.com> CentOS-regression: Gluster Build System <jenkins@build.gluster.org> Reviewed-by: Jeff Darcy <jdarcy@redhat.com>
-rw-r--r--configure.ac8
-rw-r--r--libglusterfs/src/common-utils.c28
-rw-r--r--libglusterfs/src/libglusterfs-messages.h11
-rw-r--r--libglusterfs/src/syscall.c10
-rw-r--r--libglusterfs/src/syscall.h6
-rw-r--r--xlators/storage/posix/src/posix.c37
6 files changed, 87 insertions, 13 deletions
diff --git a/configure.ac b/configure.ac
index 6917322bd58..28ff3e5a3f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -981,6 +981,13 @@ if test "x${have_posix_fallocate}" = "xyes"; then
AC_DEFINE(HAVE_POSIX_FALLOCATE, 1, [define if posix_fallocate exists])
fi
+BUILD_NANOSECOND_TIMESTAMPS=no
+AC_CHECK_FUNC([utimensat], [have_utimensat=yes])
+if test "x${have_utimensat}" = "xyes"; then
+ BUILD_NANOSECOND_TIMESTAMPS=yes
+ AC_DEFINE(HAVE_UTIMENSAT, 1, [define if utimensat exists])
+fi
+
OLD_CFLAGS=${CFLAGS}
CFLAGS="-D_GNU_SOURCE"
AC_CHECK_DECL([SEEK_HOLE], , , [#include <unistd.h>])
@@ -1544,4 +1551,5 @@ echo "Experimental xlators : $BUILD_EXPERIMENTAL"
echo "Events : $BUILD_EVENTS"
echo "EC dynamic support : $EC_DYNAMIC_SUPPORT"
echo "Use memory pools : $USE_MEMPOOL"
+echo "Nanosecond m/atimes : $BUILD_NANOSECOND_TIMESTAMPS"
echo
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;
}
diff --git a/libglusterfs/src/libglusterfs-messages.h b/libglusterfs/src/libglusterfs-messages.h
index 29196929eb3..23ed7b727d3 100644
--- a/libglusterfs/src/libglusterfs-messages.h
+++ b/libglusterfs/src/libglusterfs-messages.h
@@ -37,7 +37,7 @@
#define GLFS_LG_BASE GLFS_MSGID_COMP_LIBGLUSTERFS
-#define GLFS_LG_NUM_MESSAGES 209
+#define GLFS_LG_NUM_MESSAGES 210
#define GLFS_LG_MSGID_END (GLFS_LG_BASE + GLFS_LG_NUM_MESSAGES + 1)
/* Messaged with message IDs */
@@ -1791,6 +1791,15 @@
* @recommendedaction
*
*/
+
+#define LG_MSG_UTIMENSAT_FAILED (GLFS_LG_BASE + 210)
+
+/*!
+ * @messageid
+ * @diagnosis
+ * @recommendedaction
+ *
+ */
/*------------*/
#define glfs_msg_end_lg GLFS_LG_MSGID_END, "Invalid: End of messages"
diff --git a/libglusterfs/src/syscall.c b/libglusterfs/src/syscall.c
index 4e4c6a728da..a7d4402808d 100644
--- a/libglusterfs/src/syscall.c
+++ b/libglusterfs/src/syscall.c
@@ -272,6 +272,16 @@ sys_utimes (const char *filename, const struct timeval times[2])
}
+#if defined(HAVE_UTIMENSAT)
+int
+sys_utimensat (int dirfd, const char *filename, const struct timespec times[2],
+ int flags)
+{
+ return utimensat (dirfd, filename, times, flags);
+}
+#endif
+
+
int
sys_creat (const char *pathname, mode_t mode)
{
diff --git a/libglusterfs/src/syscall.h b/libglusterfs/src/syscall.h
index b1bcad138c5..6bb374822ee 100644
--- a/libglusterfs/src/syscall.h
+++ b/libglusterfs/src/syscall.h
@@ -129,6 +129,12 @@ sys_ftruncate (int fd, off_t length);
int
sys_utimes (const char *filename, const struct timeval times[2]);
+#if defined(HAVE_UTIMENSAT)
+int
+sys_utimensat (int dirfd, const char *filename, const struct timespec times[2],
+ int flags);
+#endif
+
int
sys_creat (const char *pathname, mode_t mode);
diff --git a/xlators/storage/posix/src/posix.c b/xlators/storage/posix/src/posix.c
index f3fca45492b..04d141f41b2 100644
--- a/xlators/storage/posix/src/posix.c
+++ b/xlators/storage/posix/src/posix.c
@@ -84,6 +84,25 @@ extern char *marker_xattrs[];
#endif
+/* Setting microseconds or nanoseconds depending on what's supported:
+ The passed in `tv` can be
+ struct timespec
+ if supported (better, because it supports nanosecond resolution) or
+ struct timeval
+ otherwise. */
+#if HAVE_UTIMENSAT
+#define SET_TIMESPEC_NSEC_OR_TIMEVAL_USEC(tv, nanosecs) \
+ tv.tv_nsec = nanosecs
+#define PATH_SET_TIMESPEC_OR_TIMEVAL(path, tv) \
+ (sys_utimensat (AT_FDCWD, path, tv, AT_SYMLINK_NOFOLLOW))
+#else
+#define SET_TIMESPEC_NSEC_OR_TIMEVAL_USEC(tv, nanosecs) \
+ tv.tv_usec = nanosecs / 1000
+#define PATH_SET_TIMESPEC_OR_TIMEVAL(path, tv) \
+ (lutimes (path, tv))
+#endif
+
+
dict_t*
posix_dict_set_nlink (dict_t *req, dict_t *res, int32_t nlink)
{
@@ -384,7 +403,11 @@ posix_do_utimes (xlator_t *this,
int valid)
{
int32_t ret = -1;
- struct timeval tv[2] = {{0,},{0,}};
+#if defined(HAVE_UTIMENSAT)
+ struct timespec tv[2] = { {0,}, {0,} };
+#else
+ struct timeval tv[2] = { {0,}, {0,} };
+#endif
struct stat stat;
int is_symlink = 0;
@@ -400,23 +423,23 @@ posix_do_utimes (xlator_t *this,
if ((valid & GF_SET_ATTR_ATIME) == GF_SET_ATTR_ATIME) {
tv[0].tv_sec = stbuf->ia_atime;
- tv[0].tv_usec = stbuf->ia_atime_nsec / 1000;
+ SET_TIMESPEC_NSEC_OR_TIMEVAL_USEC(tv[0], stbuf->ia_atime_nsec);
} else {
/* atime is not given, use current values */
tv[0].tv_sec = ST_ATIM_SEC (&stat);
- tv[0].tv_usec = ST_ATIM_NSEC (&stat) / 1000;
+ SET_TIMESPEC_NSEC_OR_TIMEVAL_USEC(tv[0], ST_ATIM_NSEC (&stat));
}
if ((valid & GF_SET_ATTR_MTIME) == GF_SET_ATTR_MTIME) {
tv[1].tv_sec = stbuf->ia_mtime;
- tv[1].tv_usec = stbuf->ia_mtime_nsec / 1000;
+ SET_TIMESPEC_NSEC_OR_TIMEVAL_USEC(tv[1], stbuf->ia_mtime_nsec);
} else {
/* mtime is not given, use current values */
tv[1].tv_sec = ST_MTIM_SEC (&stat);
- tv[1].tv_usec = ST_MTIM_NSEC (&stat) / 1000;
+ SET_TIMESPEC_NSEC_OR_TIMEVAL_USEC(tv[1], ST_MTIM_NSEC (&stat));
}
- ret = lutimes (path, tv);
+ ret = PATH_SET_TIMESPEC_OR_TIMEVAL(path, tv);
if ((ret == -1) && (errno == ENOSYS)) {
gf_msg_debug (this->name, 0, "%s (%s)",
path, strerror (errno));
@@ -425,7 +448,7 @@ posix_do_utimes (xlator_t *this,
goto out;
}
- ret = sys_utimes (path, tv);
+ ret = PATH_SET_TIMESPEC_OR_TIMEVAL(path, tv);
}
out: