summaryrefslogtreecommitdiff
path: root/source3
diff options
context:
space:
mode:
Diffstat (limited to 'source3')
-rw-r--r--source3/configure.in51
-rw-r--r--source3/smbd/notify_hash.c38
2 files changed, 85 insertions, 4 deletions
diff --git a/source3/configure.in b/source3/configure.in
index d11acd0267..e343a9f54f 100644
--- a/source3/configure.in
+++ b/source3/configure.in
@@ -1196,6 +1196,57 @@ if test x$ac_cv_func_fstat64 = xno ; then
fi
fi
+#################################################
+# Check whether struct stat has timestamps with sub-second resolution.
+# At least IRIX and Solaris have these.
+#
+# We check that
+# all of st_mtim, st_atim and st_ctim exist
+# all of the members are in fact of type struct timespec
+#
+# There is some conflicting standards weirdness about whether we should use
+# "struct timespec" or "timespec_t". Linux doesn't have timespec_t, so we
+# prefer struct timespec.
+
+AC_CACHE_CHECK([whether struct stat has sub-second timestamps], samba_stat_hires,
+ [
+ AC_TRY_COMPILE(
+ [
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+ ],
+ [
+ struct timespec t;
+ struct stat s = {0};
+ t.tv_sec = s.st_mtim.tv_sec;
+ t.tv_nsec = s.st_mtim.tv_nsec;
+ t.tv_sec = s.st_ctim.tv_sec;
+ t.tv_nsec = s.st_ctim.tv_nsec;
+ t.tv_sec = s.st_atim.tv_sec;
+ t.tv_nsec = s.st_atim.tv_nsec;
+ ],
+ samba_stat_hires=yes, samba_stat_hires=no)
+ ])
+
+if test x"$samba_stat_hires" = x"yes" ; then
+ AC_DEFINE(HAVE_STAT_ST_MTIM, 1, [whether struct stat contains st_mtim])
+ AC_DEFINE(HAVE_STAT_ST_ATIM, 1, [whether struct stat contains st_atim])
+ AC_DEFINE(HAVE_STAT_ST_CTIM, 1, [whether struct stat contains st_ctim])
+ AC_DEFINE(HAVE_STAT_HIRES_TIMESTAMPS, 1,
+ [whether struct stat has sub-second timestamps])
+fi
+
#####################################
# we might need the resolv library on some systems
AC_CHECK_LIB(resolv, dn_expand)
diff --git a/source3/smbd/notify_hash.c b/source3/smbd/notify_hash.c
index 08eefab652..ee7d4314ee 100644
--- a/source3/smbd/notify_hash.c
+++ b/source3/smbd/notify_hash.c
@@ -23,14 +23,28 @@
struct change_data {
time_t last_check_time; /* time we last checked this entry */
+#ifdef HAVE_STAT_HIRES_TIMESTAMPS
+ struct timespec modify_time;
+ struct timespec status_time;
+#else
time_t modify_time; /* Info from the directory we're monitoring. */
time_t status_time; /* Info from the directory we're monitoring. */
+#endif
time_t total_time; /* Total time of all directory entries - don't care if it wraps. */
unsigned int num_entries; /* Zero or the number of files in the directory. */
unsigned int mode_sum;
unsigned char name_hash[16];
};
+
+#ifdef HAVE_STAT_HIRES_TIMESTAMPS
+/* Compare struct timespec. */
+#define TIMESTAMP_NEQ(x, y) (((x).tv_sec != (y).tv_sec) || ((x).tv_nsec != (y).tv_nsec))
+#else
+/* Compare time_t . */
+#define TIMESTAMP_NEQ(x, y) ((x) != (y))
+#endif
+
/****************************************************************************
Create the hash we will use to determine if the contents changed.
*****************************************************************************/
@@ -52,20 +66,36 @@ static BOOL notify_hash(connection_struct *conn, char *path, uint32 flags,
if(SMB_VFS_STAT(conn,path, &st) == -1)
return False;
+#ifdef HAVE_STAT_HIRES_TIMESTAMPS
+ data->modify_time = st.st_mtim;
+ data->status_time = st.st_ctim;
+#else
data->modify_time = st.st_mtime;
data->status_time = st.st_ctime;
+#endif
if (old_data) {
/*
* Shortcut to avoid directory scan if the time
* has changed - we always must return true then.
*/
- if (old_data->modify_time != data->modify_time ||
- old_data->status_time != data->status_time ) {
+ if (TIMESTAMP_NEQ(old_data->modify_time, data->modify_time) ||
+ TIMESTAMP_NEQ(old_data->status_time, data->status_time) ) {
return True;
}
}
+ if (S_ISDIR(st.st_mode) &&
+ (flags & ~(FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME)) == 0)
+ {
+ /* This is the case of a client wanting to know only when
+ * the contents of a directory changes. Since any file
+ * creation, rename or deletion will update the directory
+ * timestamps, we don't need to create a hash.
+ */
+ return True;
+ }
+
/*
* If we are to watch for changes that are only stored
* in inodes of files, not in the directory inode, we must
@@ -176,8 +206,8 @@ static BOOL hash_check_notify(connection_struct *conn, uint16 vuid, char *path,
}
if (!notify_hash(conn, path, flags, &data2, data) ||
- data2.modify_time != data->modify_time ||
- data2.status_time != data->status_time ||
+ TIMESTAMP_NEQ(data2.modify_time, data->modify_time) ||
+ TIMESTAMP_NEQ(data2.status_time, data->status_time) ||
data2.total_time != data->total_time ||
data2.num_entries != data->num_entries ||
data2.mode_sum != data->mode_sum ||