From 5c7d47fb1b9f55a1316ade194dfed920bbc6d1e6 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 21 Aug 2009 22:07:44 -0700 Subject: [PATCH] Fix bug 6529 - Offline files conflict with Vista and Office 2003 On filesystems that can't store less than one second timestamps, round the incoming timestamp set requests so the client can't discover that a time set request has been truncated by the filesystem. Jeremy --- source3/include/proto.h | 1 + source3/include/smb.h | 3 +++ source3/lib/time.c | 11 +++++++++++ source3/smbd/service.c | 23 +++++++++++++++++++++++ source3/smbd/trans2.c | 8 ++++++++ 5 files changed, 46 insertions(+), 0 deletions(-) diff --git a/source3/include/proto.h b/source3/include/proto.h index 18555bc..3451a38 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -1038,6 +1038,7 @@ struct timespec timespec_current(void); struct timespec timespec_min(const struct timespec *ts1, const struct timespec *ts2); int timespec_compare(const struct timespec *ts1, const struct timespec *ts2); +void round_timespec(struct timespec *ts); struct timespec interpret_long_date(const char *p); void cli_put_dos_date(struct cli_state *cli, char *buf, int offset, time_t unixdate); void cli_put_dos_date2(struct cli_state *cli, char *buf, int offset, time_t unixdate); diff --git a/source3/include/smb.h b/source3/include/smb.h index b20a8ef..cb11cdb 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -559,6 +559,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/source3/lib/time.c b/source3/lib/time.c index 865456b..12bf2c2 100644 --- a/source3/lib/time.c +++ b/source3/lib/time.c @@ -713,6 +713,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/source3/smbd/service.c b/source3/smbd/service.c index fc59744..a406adc 100644 --- a/source3/smbd/service.c +++ b/source3/smbd/service.c @@ -1008,6 +1008,29 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, goto err_root_exit; } + { + 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/source3/smbd/trans2.c b/source3/smbd/trans2.c index 7173796..e928369 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -4941,6 +4941,14 @@ 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(&ft->create_time); + round_timespec(&ft->atime); + round_timespec(&ft->mtime); + } + if (setting_write_time) { /* * This was a Windows setfileinfo on an open file. -- 1.6.0.4