diff --git a/source/include/smb.h b/source/include/smb.h index 7484efd..9999682 100644 --- a/source/include/smb.h +++ b/source/include/smb.h @@ -652,6 +652,9 @@ typedef struct connection_struct { BOOL ipc; BOOL read_only; /* Attributes for the current user of the share. */ BOOL admin_user; /* Attributes for the current user of the share. */ + BOOL hires_timestamps_avail; /* Does this filesystem honor + sub second timestamps on files + and directories ? */ char *dirpath; char *connectpath; char *origpath; diff --git a/source/lib/time.c b/source/lib/time.c index 8e28fcc..d3de771 100644 --- a/source/lib/time.c +++ b/source/lib/time.c @@ -1145,6 +1145,17 @@ int timespec_compare(const struct timespec *ts1, const struct timespec *ts2) } /**************************************************************************** + Round up a timespec if nsec > 500000000, round down if lower, + then zero nsec. +****************************************************************************/ + +void round_timespec(struct timespec *ts) +{ + ts->tv_sec += ts->tv_nsec >= 500000000 ? 1 : 0; + ts->tv_nsec = 0; +} + +/**************************************************************************** Interprets an nt time into a unix struct timespec. Differs from nt_time_to_unix in that an 8 byte value of 0xffffffffffffffff will be returned as (time_t)-1, whereas nt_time_to_unix returns 0 in this case. diff --git a/source/smbd/service.c b/source/smbd/service.c index bfe9649..4386458 100644 --- a/source/smbd/service.c +++ b/source/smbd/service.c @@ -1010,7 +1010,30 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, *status = NT_STATUS_BAD_NETWORK_NAME; return NULL; } - + + { + struct timespec atime_ts = get_atimespec(&st); + struct timespec ctime_ts = get_ctimespec(&st); + struct timespec mtime_ts = get_mtimespec(&st); + + if (mtime_ts.tv_nsec || + atime_ts.tv_nsec || + ctime_ts.tv_nsec) { + /* If any of the normal UNIX directory timestamps + * have a non-zero tv_nsec component assume + * we can fully store hires timestamps. We need + * to make a runtime/share level distinction + * as on Linux ext3 doesn't have hires timestamps, but + * ext4 does, so a compile time test won't work. JRA. + */ + DEBUG(10,("make_connection_snum: hires timestamps " + "available on share %s, directory %s\n", + lp_servicename(snum), + conn->connectpath )); + conn->hires_timestamps_avail = true; + } + } + string_set(&conn->origpath,conn->connectpath); #if SOFTLINK_OPTIMISATION diff --git a/source/smbd/trans2.c b/source/smbd/trans2.c index d3b4fb9..1399213 100644 --- a/source/smbd/trans2.c +++ b/source/smbd/trans2.c @@ -4133,6 +4133,13 @@ static NTSTATUS smb_set_file_time(connection_struct *conn, } } + if (!conn->hires_timestamps_avail) { + /* We can't store sub second timestamps + * on this share. Round to seconds. */ + round_timespec(&ts[0]); + round_timespec(&ts[1]); + } + if(fsp != NULL) { /* * This was a setfileinfo on an open file.