From 030a0484eb2496278d7775c2c0bc43acad6ac860 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 21 Aug 2009 22:24:47 -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 --- source/include/proto.h | 1 + source/include/smb.h | 3 +++ source/lib/time.c | 11 +++++++++++ source/smbd/service.c | 23 +++++++++++++++++++++++ source/smbd/trans2.c | 7 +++++++ 5 files changed, 45 insertions(+), 0 deletions(-) diff --git a/source/include/proto.h b/source/include/proto.h index ec8637b..2caba47 100644 --- a/source/include/proto.h +++ b/source/include/proto.h @@ -1176,6 +1176,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/source/include/smb.h b/source/include/smb.h index 56d9461..be284e6 100644 --- a/source/include/smb.h +++ b/source/include/smb.h @@ -583,6 +583,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 0da4b5b..6d67d2f 100644 --- a/source/lib/time.c +++ b/source/lib/time.c @@ -1230,6 +1230,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 4724dd7..32beeda 100644 --- a/source/smbd/service.c +++ b/source/smbd/service.c @@ -1080,6 +1080,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/source/smbd/trans2.c b/source/smbd/trans2.c index 160fe71..e28ce98 100644 --- a/source/smbd/trans2.c +++ b/source/smbd/trans2.c @@ -4925,6 +4925,13 @@ 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 (setting_write_time) { /* * This was a Windows setfileinfo on an open file. -- 1.6.0.4