From c72566fb524a547ad1ca06f3f20f9238908e0a79 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 9 Sep 2009 14:39:17 -0700 Subject: [PATCH] Fix bug 6529 - Offline files conflict with Vista and Office 2003. Jeremy. --- source3/include/includes.h | 6 ++ source3/include/proto.h | 5 +- source3/include/smb.h | 4 + source3/lib/time.c | 41 +++++++++++++- source3/modules/vfs_default.c | 51 ++++++++++++++++- source3/modules/vfs_onefs.c | 4 +- source3/smbd/nttrans.c | 16 +++--- source3/smbd/trans2.c | 126 ++++++++++++++++++++++------------------- 8 files changed, 182 insertions(+), 71 deletions(-) diff --git a/source3/include/includes.h b/source3/include/includes.h index 248c326..4dee258 100644 --- a/source3/include/includes.h +++ b/source3/include/includes.h @@ -526,6 +526,12 @@ struct timespec { }; #endif +enum timestamp_set_resolution { + TIMESTAMP_SET_SECONDS = 0, + TIMESTAMP_SET_MSEC, + TIMESTAMP_SET_NT_OR_BETTER +}; + #ifdef HAVE_BROKEN_GETGROUPS #define GID_T int #else diff --git a/source3/include/proto.h b/source3/include/proto.h index d33a019..5873386 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -1017,7 +1017,8 @@ char *current_timestring(TALLOC_CTX *ctx, bool hires); void srv_put_dos_date(char *buf,int offset,time_t unixdate); void srv_put_dos_date2(char *buf,int offset, time_t unixdate); void srv_put_dos_date3(char *buf,int offset,time_t unixdate); -void put_long_date_timespec(char *p, struct timespec ts); +void round_timespec(enum timestamp_set_resolution res, struct timespec *ts); +void put_long_date_timespec(enum timestamp_set_resolution res, char *p, struct timespec ts); void put_long_date(char *p, time_t t); struct timespec get_create_timespec(const SMB_STRUCT_STAT *st,bool fake_dirs); struct timespec get_atimespec(const SMB_STRUCT_STAT *pst); @@ -1038,6 +1039,8 @@ 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_to_sec(struct timespec *ts); +void round_timespec_to_usec(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..7cb8e95 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -559,6 +559,10 @@ 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. */ + /* Does this filesystem honor + sub second timestamps on files + and directories when setting time ? */ + enum timestamp_set_resolution ts_res; char *dirpath; char *connectpath; char *origpath; diff --git a/source3/lib/time.c b/source3/lib/time.c index 865456b..839ebe9 100644 --- a/source3/lib/time.c +++ b/source3/lib/time.c @@ -301,14 +301,30 @@ void srv_put_dos_date3(char *buf,int offset,time_t unixdate) put_dos_date3(buf, offset, unixdate, server_zone_offset); } +void round_timespec(enum timestamp_set_resolution res, struct timespec *ts) +{ + switch (res) { + case TIMESTAMP_SET_SECONDS: + round_timespec_to_sec(ts); + break; + case TIMESTAMP_SET_MSEC: + round_timespec_to_usec(ts); + break; + case TIMESTAMP_SET_NT_OR_BETTER: + /* No rounding needed. */ + break; + } +} + /**************************************************************************** Take a Unix time and convert to an NTTIME structure and place in buffer pointed to by p. ****************************************************************************/ -void put_long_date_timespec(char *p, struct timespec ts) +void put_long_date_timespec(enum timestamp_set_resolution res, char *p, struct timespec ts) { NTTIME nt; + round_timespec(res, &ts); unix_timespec_to_nt_time(&nt, ts); SIVAL(p, 0, nt & 0xFFFFFFFF); SIVAL(p, 4, nt >> 32); @@ -319,7 +335,7 @@ void put_long_date(char *p, time_t t) struct timespec ts; ts.tv_sec = t; ts.tv_nsec = 0; - put_long_date_timespec(p, ts); + put_long_date_timespec(TIMESTAMP_SET_SECONDS, p, ts); } /**************************************************************************** @@ -713,6 +729,27 @@ 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_to_sec(struct timespec *ts) +{ + ts->tv_sec = convert_timespec_to_time_t(*ts); + ts->tv_nsec = 0; +} + +/**************************************************************************** + Round a timespec to usec value. +****************************************************************************/ + +void round_timespec_to_usec(struct timespec *ts) +{ + struct timeval tv = convert_timespec_to_timeval(*ts); + *ts = convert_timeval_to_timespec(tv); +} + +/**************************************************************************** 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/modules/vfs_default.c b/source3/modules/vfs_default.c index aa20705..0a66531 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -92,13 +92,62 @@ static int vfswrap_statvfs(struct vfs_handle_struct *handle, const char *path, static uint32_t vfswrap_fs_capabilities(struct vfs_handle_struct *handle) { + connection_struct *conn = handle->conn; + uint32_t caps = FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES; + SMB_STRUCT_STAT st; + NTSTATUS status; + struct timespec mtime_ts, ctime_ts, atime_ts; + int ret = -1; + #if defined(DARWINOS) struct vfs_statvfs_struct statbuf; ZERO_STRUCT(statbuf); sys_statvfs(handle->conn->connectpath, &statbuf); return statbuf.FsCapabilities; #endif - return FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES; + + conn->ts_res = TIMESTAMP_SET_SECONDS; + + /* Work out what timestamp resolution we can + * use when setting a timestamp. */ + + ret = SMB_VFS_STAT(conn, conn->connectpath, &st); + if (ret == -1) { + return caps; + } + + mtime_ts = get_mtimespec(&st); + ctime_ts = get_ctimespec(&st); + atime_ts = get_atimespec(&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 might be able to set sub-second timestamps. + * See what filetime set primitives we have. + */ +#if defined(HAVE_UTIMES) + /* utimes allows msec timestamps to be set. */ + conn->ts_res = TIMESTAMP_SET_MSEC; +#elif defined(HAVE_UTIME) + /* utime only allows sec timestamps to be set. */ + conn->ts_res = TIMESTAMP_SET_SECONDS; +#endif + + /* TODO. Add a configure test for the Linux + * nsec timestamp set system call, and use it + * if available.... + */ + DEBUG(10,("vfswrap_fs_capabilities: timestamp " + "resolution of %s " + "available on share %s, directory %s\n", + conn->ts_res == TIMESTAMP_SET_MSEC ? "msec" : "sec", + lp_servicename(conn->cnum), + conn->connectpath )); + } + return caps; } /* Directory operations */ diff --git a/source3/modules/vfs_onefs.c b/source3/modules/vfs_onefs.c index e4a0feb..10c636d 100644 --- a/source3/modules/vfs_onefs.c +++ b/source3/modules/vfs_onefs.c @@ -241,7 +241,9 @@ static uint32_t onefs_fs_capabilities(struct vfs_handle_struct *handle) result |= FILE_NAMED_STREAMS; } - return result | SMB_VFS_NEXT_FS_CAPABILITIES(handle); + result |= SMB_VFS_NEXT_FS_CAPABILITIES(handle); + conn->ts_res = TIMESTAMP_SET_MSEC; + return result; } static vfs_op_tuple onefs_ops[] = { diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index 0c0bebb..9f30f06 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -614,13 +614,13 @@ void reply_ntcreate_and_X(struct smb_request *req) dos_filetime_timespec(&m_timespec); } - put_long_date_timespec(p, c_timespec); /* create time. */ + put_long_date_timespec(conn->ts_res, p, c_timespec); /* create time. */ p += 8; - put_long_date_timespec(p, a_timespec); /* access time */ + put_long_date_timespec(conn->ts_res, p, a_timespec); /* access time */ p += 8; - put_long_date_timespec(p, m_timespec); /* write time */ + put_long_date_timespec(conn->ts_res, p, m_timespec); /* write time */ p += 8; - put_long_date_timespec(p, m_timespec); /* change time */ + put_long_date_timespec(conn->ts_res, p, m_timespec); /* change time */ p += 8; SIVAL(p,0,fattr); /* File Attributes. */ p += 4; @@ -1081,13 +1081,13 @@ static void call_nt_transact_create(connection_struct *conn, dos_filetime_timespec(&m_timespec); } - put_long_date_timespec(p, c_timespec); /* create time. */ + put_long_date_timespec(conn->ts_res, p, c_timespec); /* create time. */ p += 8; - put_long_date_timespec(p, a_timespec); /* access time */ + put_long_date_timespec(conn->ts_res, p, a_timespec); /* access time */ p += 8; - put_long_date_timespec(p, m_timespec); /* write time */ + put_long_date_timespec(conn->ts_res, p, m_timespec); /* write time */ p += 8; - put_long_date_timespec(p, m_timespec); /* change time */ + put_long_date_timespec(conn->ts_res, p, m_timespec); /* change time */ p += 8; SIVAL(p,0,fattr); /* File Attributes. */ p += 4; diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 7173796..68d2c97 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -1598,10 +1598,10 @@ static bool get_lanman2_dir_entry(TALLOC_CTX *ctx, was_8_3 = mangle_is_8_3(fname, True, conn->params); p += 4; SIVAL(p,0,reskey); p += 4; - put_long_date_timespec(p,create_date_ts); p += 8; - put_long_date_timespec(p,adate_ts); p += 8; - put_long_date_timespec(p,mdate_ts); p += 8; - put_long_date_timespec(p,mdate_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,create_date_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,adate_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,mdate_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,mdate_ts); p += 8; SOFF_T(p,0,file_size); p += 8; SOFF_T(p,0,allocation_size); p += 8; SIVAL(p,0,nt_extmode); p += 4; @@ -1649,10 +1649,10 @@ static bool get_lanman2_dir_entry(TALLOC_CTX *ctx, DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_FILE_DIRECTORY_INFO\n")); p += 4; SIVAL(p,0,reskey); p += 4; - put_long_date_timespec(p,create_date_ts); p += 8; - put_long_date_timespec(p,adate_ts); p += 8; - put_long_date_timespec(p,mdate_ts); p += 8; - put_long_date_timespec(p,mdate_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,create_date_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,adate_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,mdate_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,mdate_ts); p += 8; SOFF_T(p,0,file_size); p += 8; SOFF_T(p,0,allocation_size); p += 8; SIVAL(p,0,nt_extmode); p += 4; @@ -1672,10 +1672,10 @@ static bool get_lanman2_dir_entry(TALLOC_CTX *ctx, DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_FILE_FULL_DIRECTORY_INFO\n")); p += 4; SIVAL(p,0,reskey); p += 4; - put_long_date_timespec(p,create_date_ts); p += 8; - put_long_date_timespec(p,adate_ts); p += 8; - put_long_date_timespec(p,mdate_ts); p += 8; - put_long_date_timespec(p,mdate_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,create_date_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,adate_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,mdate_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,mdate_ts); p += 8; SOFF_T(p,0,file_size); p += 8; SOFF_T(p,0,allocation_size); p += 8; SIVAL(p,0,nt_extmode); p += 4; @@ -1721,10 +1721,10 @@ static bool get_lanman2_dir_entry(TALLOC_CTX *ctx, DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_ID_FULL_DIRECTORY_INFO\n")); p += 4; SIVAL(p,0,reskey); p += 4; - put_long_date_timespec(p,create_date_ts); p += 8; - put_long_date_timespec(p,adate_ts); p += 8; - put_long_date_timespec(p,mdate_ts); p += 8; - put_long_date_timespec(p,mdate_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,create_date_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,adate_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,mdate_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,mdate_ts); p += 8; SOFF_T(p,0,file_size); p += 8; SOFF_T(p,0,allocation_size); p += 8; SIVAL(p,0,nt_extmode); p += 4; @@ -1754,10 +1754,10 @@ static bool get_lanman2_dir_entry(TALLOC_CTX *ctx, was_8_3 = mangle_is_8_3(fname, True, conn->params); p += 4; SIVAL(p,0,reskey); p += 4; - put_long_date_timespec(p,create_date_ts); p += 8; - put_long_date_timespec(p,adate_ts); p += 8; - put_long_date_timespec(p,mdate_ts); p += 8; - put_long_date_timespec(p,mdate_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,create_date_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,adate_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,mdate_ts); p += 8; + put_long_date_timespec(conn->ts_res, p,mdate_ts); p += 8; SOFF_T(p,0,file_size); p += 8; SOFF_T(p,0,allocation_size); p += 8; SIVAL(p,0,nt_extmode); p += 4; @@ -3526,9 +3526,9 @@ static char *store_file_unix_basic(connection_struct *conn, SOFF_T(pdata,0,SMB_VFS_GET_ALLOC_SIZE(conn,fsp,psbuf)); /* Number of bytes used on disk - 64 Bit */ pdata += 8; - put_long_date_timespec(pdata,get_ctimespec(psbuf)); /* Change Time 64 Bit */ - put_long_date_timespec(pdata+8,get_atimespec(psbuf)); /* Last access time 64 Bit */ - put_long_date_timespec(pdata+16,get_mtimespec(psbuf)); /* Last modification time 64 Bit */ + put_long_date_timespec(TIMESTAMP_SET_NT_OR_BETTER, pdata,get_ctimespec(psbuf)); /* Change Time 64 Bit */ + put_long_date_timespec(TIMESTAMP_SET_NT_OR_BETTER, pdata+8,get_atimespec(psbuf)); /* Last access time 64 Bit */ + put_long_date_timespec(TIMESTAMP_SET_NT_OR_BETTER, pdata+16,get_mtimespec(psbuf)); /* Last modification time 64 Bit */ pdata += 24; SIVAL(pdata,0,psbuf->st_uid); /* user id for the owner */ @@ -3668,7 +3668,7 @@ static char *store_file_unix_basic_info2(connection_struct *conn, pdata = store_file_unix_basic(conn, pdata, fsp, psbuf); /* Create (birth) time 64 bit */ - put_long_date_timespec(pdata, get_create_timespec(psbuf, False)); + put_long_date_timespec(TIMESTAMP_SET_NT_OR_BETTER, pdata, get_create_timespec(psbuf, False)); pdata += 8; map_info2_flags_from_sbuf(psbuf, &file_flags, &flags_mask); @@ -4309,10 +4309,10 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd data_size = 40; SIVAL(pdata,36,0); } - put_long_date_timespec(pdata,create_time_ts); - put_long_date_timespec(pdata+8,atime_ts); - put_long_date_timespec(pdata+16,mtime_ts); /* write time */ - put_long_date_timespec(pdata+24,mtime_ts); /* change time */ + put_long_date_timespec(conn->ts_res, pdata,create_time_ts); + put_long_date_timespec(conn->ts_res, pdata+8,atime_ts); + put_long_date_timespec(conn->ts_res, pdata+16,mtime_ts); /* write time */ + put_long_date_timespec(conn->ts_res, pdata+24,mtime_ts); /* change time */ SIVAL(pdata,32,mode); DEBUG(5,("SMB_QFBI - ")); @@ -4399,10 +4399,10 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd { unsigned int ea_size = estimate_ea_size(conn, fsp, fname); DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_ALL_INFORMATION\n")); - put_long_date_timespec(pdata,create_time_ts); - put_long_date_timespec(pdata+8,atime_ts); - put_long_date_timespec(pdata+16,mtime_ts); /* write time */ - put_long_date_timespec(pdata+24,mtime_ts); /* change time */ + put_long_date_timespec(conn->ts_res, pdata,create_time_ts); + put_long_date_timespec(conn->ts_res, pdata+8,atime_ts); + put_long_date_timespec(conn->ts_res, pdata+16,mtime_ts); /* write time */ + put_long_date_timespec(conn->ts_res, pdata+24,mtime_ts); /* change time */ SIVAL(pdata,32,mode); SIVAL(pdata,36,0); /* padding. */ pdata += 40; @@ -4537,10 +4537,10 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd case SMB_FILE_NETWORK_OPEN_INFORMATION: DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_NETWORK_OPEN_INFORMATION\n")); - put_long_date_timespec(pdata,create_time_ts); - put_long_date_timespec(pdata+8,atime_ts); - put_long_date_timespec(pdata+16,mtime_ts); /* write time */ - put_long_date_timespec(pdata+24,mtime_ts); /* change time */ + put_long_date_timespec(conn->ts_res, pdata,create_time_ts); + put_long_date_timespec(conn->ts_res, pdata+8,atime_ts); + put_long_date_timespec(conn->ts_res, pdata+16,mtime_ts); /* write time */ + put_long_date_timespec(conn->ts_res, pdata+24,mtime_ts); /* change time */ SOFF_T(pdata,32,allocation_size); SOFF_T(pdata,40,file_size); SIVAL(pdata,48,mode); @@ -4894,15 +4894,22 @@ NTSTATUS smb_set_file_time(connection_struct *conn, struct smb_file_time *ft, bool setting_write_time) { + struct smb_file_time ft_stat; uint32 action = FILE_NOTIFY_CHANGE_LAST_ACCESS - |FILE_NOTIFY_CHANGE_LAST_WRITE; + |FILE_NOTIFY_CHANGE_LAST_WRITE + |FILE_NOTIFY_CHANGE_CREATION; if (!VALID_STAT(*psbuf)) { return NT_STATUS_OBJECT_NAME_NOT_FOUND; } /* get some defaults (no modifications) if any info is zero or -1. */ + if (null_timespec(ft->create_time)) { + ft->create_time = get_create_timespec(psbuf, lp_fake_dir_create_times(SNUM(conn))); + action &= ~FILE_NOTIFY_CHANGE_CREATION; + } + if (null_timespec(ft->atime)) { ft->atime= get_atimespec(psbuf); action &= ~FILE_NOTIFY_CHANGE_LAST_ACCESS; @@ -4918,28 +4925,19 @@ NTSTATUS smb_set_file_time(connection_struct *conn, action &= ~FILE_NOTIFY_CHANGE_LAST_WRITE; } + /* Ensure the resolution is the correct for + * what we can store on this filesystem. */ + + round_timespec(conn->ts_res, &ft->create_time); + round_timespec(conn->ts_res, &ft->atime); + round_timespec(conn->ts_res, &ft->mtime); + DEBUG(5,("smb_set_filetime: actime: %s\n ", time_to_asc(convert_timespec_to_time_t(ft->atime)))); DEBUG(5,("smb_set_filetime: modtime: %s\n ", time_to_asc(convert_timespec_to_time_t(ft->mtime)))); - if (!null_timespec(ft->create_time)) { - DEBUG(5,("smb_set_file_time: createtime: %s\n ", - time_to_asc(convert_timespec_to_time_t(ft->create_time)))); - } - - /* - * Try and set the times of this file if - * they are different from the current values. - */ - - { - struct timespec mts = get_mtimespec(psbuf); - struct timespec ats = get_atimespec(psbuf); - if ((timespec_compare(&ft->atime, &ats) == 0) && - (timespec_compare(&ft->mtime, &mts) == 0)) { - return NT_STATUS_OK; - } - } + DEBUG(5,("smb_set_file_time: createtime: %s\n ", + time_to_asc(convert_timespec_to_time_t(ft->create_time)))); if (setting_write_time) { /* @@ -4969,14 +4967,26 @@ NTSTATUS smb_set_file_time(connection_struct *conn, } } - DEBUG(10,("smb_set_file_time: setting utimes to modified values.\n")); + ft_stat.create_time = get_create_timespec(psbuf, + lp_fake_dir_create_times(SNUM(conn))); + ft_stat.atime= get_atimespec(psbuf); + ft_stat.mtime = get_mtimespec(psbuf); + + round_timespec(conn->ts_res, &ft_stat.create_time); + round_timespec(conn->ts_res, &ft_stat.atime); + round_timespec(conn->ts_res, &ft_stat.mtime); if (fsp && fsp->base_fsp) { fname = fsp->base_fsp->fsp_name; } - if(file_ntimes(conn, fname, ft)!=0) { - return map_nt_error_from_unix(errno); + if (timespec_compare(&ft_stat.create_time, &ft->create_time) || + timespec_compare(&ft_stat.atime, &ft->atime) || + timespec_compare(&ft_stat.mtime, &ft->mtime)) { + DEBUG(10,("smb_set_file_time: setting utimes to modified values.\n")); + if(file_ntimes(conn, fname, ft)!=0) { + return map_nt_error_from_unix(errno); + } } notify_fname(conn, NOTIFY_ACTION_MODIFIED, action, fname); -- 1.5.4.3