smbd: implement the strange write time update logic
authorStefan Metzmacher <metze@samba.org>
Wed, 12 Mar 2008 14:39:38 +0000 (15:39 +0100)
committerKarolin Seeger <kseeger@samba.org>
Sat, 12 Apr 2008 05:48:24 +0000 (07:48 +0200)
We now never call file_ntimes() directly, every update
is done via smb_set_file_time().

This let samba3 pass the BASE-DELAYWRITE test.

The write time is only updated 2 seconds after the
first write() on any open handle to the current time
(not the time of the first write).

Each handle which had write requests updates the write
time to the current time on close().

If the write time is set explicit via setfileinfo or setpathinfo
the write time is visible directly and a following close
on the same handle doesn't update the write time.

metze
(cherry picked from commit 2eab212ea2e1bfd8fa716c2c89b2c042f7ba12ea)

source/include/smb.h
source/smbd/close.c
source/smbd/dir.c
source/smbd/dosmode.c
source/smbd/fileio.c
source/smbd/files.c
source/smbd/nttrans.c
source/smbd/reply.c
source/smbd/trans2.c

index d4bbe2d0ffc57445548063de3db1ea10b0afd32f..70e980a12ce447537642e0975eb033e176c9d860 100644 (file)
@@ -485,9 +485,13 @@ typedef struct files_struct {
        struct timeval open_time;
        uint32 access_mask;             /* NTCreateX access bits (FILE_READ_DATA etc.) */
        uint32 share_access;            /* NTCreateX share constants (FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE). */
-       bool pending_modtime_owner;
-       struct timespec pending_modtime;
-       struct timespec last_write_time;
+
+       bool update_write_time_triggered;
+       struct timed_event *update_write_time_event;
+       bool update_write_time_on_close;
+       struct timespec close_write_time;
+       bool write_time_forced;
+
        int oplock_type;
        int sent_oplock_break;
        struct timed_event *oplock_timeout;
index 8a5c82cc937261ece6070b5551d997f7219f6a1a..3afc037f69cd6d50ac28c4b5cee484052a3e0b33 100644 (file)
@@ -255,6 +255,10 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
                return NT_STATUS_INVALID_PARAMETER;
        }
 
+       if (fsp->write_time_forced) {
+               set_close_write_time(fsp, lck->changed_write_time);
+       }
+
        if (!del_share_mode(lck, fsp)) {
                DEBUG(0, ("close_remove_share_mode: Could not delete share "
                          "entry for file %s\n", fsp->fsp_name));
@@ -317,6 +321,11 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
        DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set "
                 "- deleting file.\n", fsp->fsp_name));
 
+       /*
+        * Don't try to update the write time when we delete the file
+        */
+       fsp->update_write_time_on_close = false;
+
        if (!unix_token_equal(lck->delete_token, &current_user.ut)) {
                /* Become the user who requested the delete. */
 
@@ -428,6 +437,66 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
        return status;
 }
 
+void set_close_write_time(struct files_struct *fsp, struct timespec ts)
+{
+       DEBUG(6,("close_write_time: %s" , time_to_asc(convert_timespec_to_time_t(ts))));
+
+       if (null_timespec(ts)) {
+               return;
+       }
+       /*
+        * if the write time on close is explict set, then don't
+        * need to fix it up to the value in the locking db
+        */
+       fsp->write_time_forced = false;
+
+       fsp->update_write_time_on_close = true;
+       fsp->close_write_time = ts;
+}
+
+static NTSTATUS update_write_time_on_close(struct files_struct *fsp)
+{
+       SMB_STRUCT_STAT sbuf;
+       struct timespec ts[2];
+       NTSTATUS status;
+
+       ZERO_STRUCT(sbuf);
+       ZERO_STRUCT(ts);
+
+       if (!fsp->update_write_time_on_close) {
+               return NT_STATUS_OK;
+       }
+
+       if (null_timespec(fsp->close_write_time)) {
+               fsp->close_write_time = timespec_current();
+       }
+
+       /* Ensure we have a valid stat struct for the source. */
+       if (fsp->fh->fd != -1) {
+               if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
+                       return map_nt_error_from_unix(errno);
+               }
+       } else {
+               if (SMB_VFS_STAT(fsp->conn,fsp->fsp_name,&sbuf) == -1) {
+                       return map_nt_error_from_unix(errno);
+               }
+       }
+
+       if (!VALID_STAT(sbuf)) {
+               /* if it doesn't seem to be a real file */
+               return NT_STATUS_OK;
+       }
+
+       ts[1] = fsp->close_write_time;
+       status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name,
+                                  &sbuf, ts, true);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       return NT_STATUS_OK;
+}
+
 /****************************************************************************
  Close a file.
 
@@ -442,6 +511,7 @@ static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_
        NTSTATUS saved_status1 = NT_STATUS_OK;
        NTSTATUS saved_status2 = NT_STATUS_OK;
        NTSTATUS saved_status3 = NT_STATUS_OK;
+       NTSTATUS saved_status4 = NT_STATUS_OK;
        connection_struct *conn = fsp->conn;
 
        if (fsp->aio_write_behind) {
@@ -496,11 +566,7 @@ static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_
         * Ensure pending modtime is set after close.
         */
 
-       if (fsp->pending_modtime_owner && !null_timespec(fsp->pending_modtime)) {
-               set_filetime(conn, fsp->fsp_name, fsp->pending_modtime);
-       } else if (!null_timespec(fsp->last_write_time)) {
-               set_filetime(conn, fsp->fsp_name, fsp->last_write_time);
-       }
+       saved_status4 = update_write_time_on_close(fsp);
 
        if (NT_STATUS_IS_OK(status)) {
                if (!NT_STATUS_IS_OK(saved_status1)) {
@@ -509,6 +575,8 @@ static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_
                        status = saved_status2;
                } else if (!NT_STATUS_IS_OK(saved_status3)) {
                        status = saved_status3;
+               } else if (!NT_STATUS_IS_OK(saved_status4)) {
+                       status = saved_status4;
                }
        }
 
index ca6f8bfd8dc22a2690afaf177f6a6c559fda2aaa..8531d6250da5bd5c7d172e046b28173bf0220204 100644 (file)
@@ -841,6 +841,8 @@ bool get_dir_entry(TALLOC_CTX *ctx,
                    mask_match_search(filename,mask,False) ||
                    mangle_mask_match(conn,filename,mask)) {
                        char mname[13];
+                       struct timespec write_time_ts;
+                       struct file_id fileid;
 
                        if (!mangle_is_8_3(filename, False, conn->params)) {
                                if (!name_to_8_3(filename,mname,False,
@@ -883,6 +885,12 @@ bool get_dir_entry(TALLOC_CTX *ctx,
                        *size = sbuf.st_size;
                        *date = sbuf.st_mtime;
 
+                       fileid = vfs_file_id_from_sbuf(conn, &sbuf);
+                       write_time_ts = get_write_time(fileid);
+                       if (!null_timespec(write_time_ts)) {
+                               *date = convert_timespec_to_time_t(write_time_ts);
+                       }
+
                        DEBUG(3,("get_dir_entry mask=[%s] found %s "
                                "fname=%s (%s)\n",
                                mask,
index a2e617c117d95be2c02f3358438ddfd7228441c6..0ac3873275b17688b7e0a260a9d4eb47940433f2 100644 (file)
@@ -571,6 +571,11 @@ int file_ntimes(connection_struct *conn, const char *fname, const struct timespe
        errno = 0;
        ZERO_STRUCT(sbuf);
 
+       DEBUG(6, ("file_ntime: actime: %s",
+                 time_to_asc(convert_timespec_to_time_t(ts[0]))));
+       DEBUG(6, ("file_ntime: modtime: %s",
+                 time_to_asc(convert_timespec_to_time_t(ts[1]))));
+
        /* Don't update the time on read-only shares */
        /* We need this as set_filetime (which can be called on
           close and other paths) can end up calling this function
@@ -615,26 +620,35 @@ int file_ntimes(connection_struct *conn, const char *fname, const struct timespe
  Change a filetime - possibly allowing DOS semantics.
 *******************************************************************/
 
-bool set_filetime(connection_struct *conn, const char *fname,
-               const struct timespec mtime)
+bool set_write_time_path(connection_struct *conn, const char *fname,
+                        struct file_id fileid, const struct timespec mtime,
+                        bool overwrite)
 {
-       struct timespec ts[2];
-
        if (null_timespec(mtime)) {
-               return(True);
+               return true;
        }
 
-       ts[1] = mtime; /* mtime. */
-       ts[0] = ts[1]; /* atime. */
-
-       if (file_ntimes(conn, fname, ts)) {
-               DEBUG(4,("set_filetime(%s) failed: %s\n",
-                                       fname,strerror(errno)));
-               return False;
+       if (!set_write_time(fileid, mtime, overwrite)) {
+               return false;
        }
 
-       notify_fname(conn, NOTIFY_ACTION_MODIFIED,
-               FILE_NOTIFY_CHANGE_LAST_WRITE, fname);
+       /* in the overwrite case the caller should trigger the notify */
+       if (!overwrite) {
+               notify_fname(conn, NOTIFY_ACTION_MODIFIED,
+                            FILE_NOTIFY_CHANGE_LAST_WRITE, fname);
+       }
 
        return true;
 }
+
+bool set_write_time_fsp(struct files_struct *fsp, const struct timespec mtime,
+                       bool overwrite)
+{
+       if (overwrite) {
+               fsp->write_time_forced = true;
+               TALLOC_FREE(fsp->update_write_time_event);
+       }
+
+       return set_write_time_path(fsp->conn, fsp->fsp_name, fsp->file_id,
+                                  mtime, overwrite);
+}
index 8cea4989f5b4dd16f5d3ad26208e951094a1e908..93a303f6c867e232d64508a2c7d8020d9ca1c3d6 100644 (file)
@@ -142,27 +142,6 @@ static ssize_t real_write_file(struct smb_request *req,
        if (ret != -1) {
                fsp->fh->pos += ret;
 
-               /*
-                * It turns out that setting the last write time from a Windows
-                * client stops any subsequent writes from updating the write time.
-                * Doing this after the write gives a race condition here where
-                * a stat may see the changed write time before we reset it here,
-                * but it's cheaper than having to store the write time in shared
-                * memory and look it up using dev/inode across all running smbd's.
-                * The 99% solution will hopefully be good enough in this case. JRA.
-                */
-
-               if (!null_timespec(fsp->pending_modtime)) {
-                       set_filetime(fsp->conn, fsp->fsp_name,
-                                       fsp->pending_modtime);
-
-                       /* If we didn't get the "set modtime" call ourselves, we must
-                          store the last write time to restore on close. JRA. */
-                       if (!fsp->pending_modtime_owner) {
-                               fsp->last_write_time = timespec_current();
-                       }
-               }
-
 /* Yes - this is correct - writes don't update this. JRA. */
 /* Found by Samba4 tests. */
 #if 0
@@ -192,6 +171,41 @@ static int wcp_file_size_change(files_struct *fsp)
        return ret;
 }
 
+static void update_write_time_handler(struct event_context *ctx,
+                                     struct timed_event *te,
+                                     const struct timeval *now,
+                                     void *private_data)
+{
+       files_struct *fsp = (files_struct *)private_data;
+
+       /* Remove the timed event handler. */
+       TALLOC_FREE(fsp->update_write_time_event);
+       DEBUG(5, ("Update write time on %s\n", fsp->fsp_name));
+
+       /* change the write time if not already changed by someoneelse */
+       set_write_time_fsp(fsp, timespec_current(), false);
+}
+
+void trigger_write_time_update(struct files_struct *fsp)
+{
+       if (fsp->write_time_forced) {
+               return;
+       }
+
+       if (fsp->update_write_time_triggered) {
+               return;
+       }
+       fsp->update_write_time_triggered = true;
+
+       /* trigger the update 2 seconds later */
+       fsp->update_write_time_on_close = true;
+       fsp->update_write_time_event =
+               event_add_timed(smbd_event_context(), NULL,
+                               timeval_current_ofs(2, 0),
+                               "update_write_time_handler",
+                               update_write_time_handler, fsp);
+}
+
 /****************************************************************************
  Write to a file.
 ****************************************************************************/
@@ -230,7 +244,9 @@ ssize_t write_file(struct smb_request *req,
                fsp->modified = True;
 
                if (SMB_VFS_FSTAT(fsp, &st) == 0) {
-                       int dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st);
+                       int dosmode;
+                       trigger_write_time_update(fsp);
+                       dosmode = dos_mode(fsp->conn,fsp->fsp_name,&st);
                        if ((lp_store_dos_attributes(SNUM(fsp->conn)) ||
                                        MAP_ARCHIVE(fsp->conn)) &&
                                        !IS_DOS_ARCHIVE(dosmode)) {
index 95f01b88ce185945d31a753125192fea609d2c31..d6e91c67be12c87e92626aaca7ef15039e844e27 100644 (file)
@@ -374,29 +374,6 @@ files_struct *file_find_print(void)
        return NULL;
 }
 
-/****************************************************************************
- Set a pending modtime across all files with a given dev/ino pair.
- Record the owner of that modtime.
-****************************************************************************/
-
-void fsp_set_pending_modtime(files_struct *tfsp, const struct timespec mod)
-{
-       files_struct *fsp;
-
-       if (null_timespec(mod)) {
-               return;
-       }
-
-       for (fsp = Files;fsp;fsp=fsp->next) {
-               if ( fsp->fh->fd != -1 && file_id_equal(&fsp->file_id, &tfsp->file_id)) {
-                       fsp->pending_modtime = mod;
-                       fsp->pending_modtime_owner = False;
-               }
-       }
-
-       tfsp->pending_modtime_owner = True;
-}
-
 /****************************************************************************
  Sync open files on a connection.
 ****************************************************************************/
@@ -441,6 +418,9 @@ void file_free(files_struct *fsp)
        /* Ensure this event will never fire. */
        TALLOC_FREE(fsp->oplock_timeout);
 
+       /* Ensure this event will never fire. */
+       TALLOC_FREE(fsp->update_write_time_event);
+
        bitmap_clear(file_bmap, fsp->fnum - FILE_HANDLE_OFFSET);
        files_used--;
 
@@ -548,9 +528,6 @@ NTSTATUS dup_file_fsp(files_struct *fsp,
        dup_fsp->open_time = fsp->open_time;
        dup_fsp->access_mask = access_mask;
        dup_fsp->share_access = share_access;
-       dup_fsp->pending_modtime_owner = fsp->pending_modtime_owner;
-       dup_fsp->pending_modtime = fsp->pending_modtime;
-       dup_fsp->last_write_time = fsp->last_write_time;
        dup_fsp->oplock_type = fsp->oplock_type;
        dup_fsp->can_lock = fsp->can_lock;
        dup_fsp->can_read = (access_mask & (FILE_READ_DATA)) ? True : False;
index 6bcf499516e87ab67c963d36148b4394170790a0..e8dae705b05b6b22469588ea8301f74aff69d32d 100644 (file)
@@ -1233,7 +1233,7 @@ static NTSTATUS copy_internals(TALLOC_CTX *ctx,
        close_file(fsp1,NORMAL_CLOSE);
 
        /* Ensure the modtime is set correctly on the destination file. */
-       fsp_set_pending_modtime(fsp2, get_mtimespec(&sbuf1));
+       set_close_write_time(fsp2, get_mtimespec(&sbuf1));
 
        status = close_file(fsp2,NORMAL_CLOSE);
 
index b300c09f4f8584d1bc2c7c68c11b44271136d018..411eb98ac5b1e509bf92cd813c75b06eef8ac166 100644 (file)
@@ -1042,6 +1042,7 @@ void reply_getatr(struct smb_request *req)
 
 void reply_setatr(struct smb_request *req)
 {
+       struct timespec ts[2];
        connection_struct *conn = req->conn;
        char *fname = NULL;
        int mode;
@@ -1053,6 +1054,8 @@ void reply_setatr(struct smb_request *req)
 
        START_PROFILE(SMBsetatr);
 
+       ZERO_STRUCT(ts);
+
        if (req->wct < 2) {
                reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
                return;
@@ -1110,7 +1113,10 @@ void reply_setatr(struct smb_request *req)
        mode = SVAL(req->inbuf,smb_vwv0);
        mtime = srv_make_unix_date3(req->inbuf+smb_vwv1);
 
-       if (!set_filetime(conn,fname,convert_time_t_to_timespec(mtime))) {
+       ts[1] = convert_time_t_to_timespec(mtime);
+       status = smb_set_file_time(conn, NULL, fname,
+                                  &sbuf, ts, true);
+       if (!NT_STATUS_IS_OK(status)) {
                reply_unixerror(req, ERRDOS, ERRnoaccess);
                END_PROFILE(SMBsetatr);
                return;
@@ -1985,7 +1991,12 @@ void reply_mknew(struct smb_request *req)
        }
 
        ts[0] = get_atimespec(&sbuf); /* atime. */
-       file_ntimes(conn, fsp->fsp_name, ts);
+       status = smb_set_file_time(conn, fsp, fname, &sbuf, ts, true);
+       if (!NT_STATUS_IS_OK(status)) {
+               END_PROFILE(SMBcreate);
+               reply_openerror(req, status);
+               return;
+       }
 
        reply_outbuf(req, 1, 0);
        SSVAL(req->outbuf,smb_vwv0,fsp->fnum);
@@ -4239,6 +4250,7 @@ void reply_close(struct smb_request *req)
                DEBUG(3,("close directory fnum=%d\n", fsp->fnum));
                status = close_file(fsp,NORMAL_CLOSE);
        } else {
+               time_t t;
                /*
                 * Close ordinary file.
                 */
@@ -4251,9 +4263,8 @@ void reply_close(struct smb_request *req)
                 * Take care of any time sent in the close.
                 */
 
-               fsp_set_pending_modtime(fsp, convert_time_t_to_timespec(
-                                               srv_make_unix_date3(
-                                                       req->inbuf+smb_vwv1)));
+               t = srv_make_unix_date3(req->inbuf+smb_vwv1);
+               set_close_write_time(fsp, convert_time_t_to_timespec(t));
 
                /*
                 * close_file() returns the unix errno if an error
@@ -4326,8 +4337,8 @@ void reply_writeclose(struct smb_request *req)
   
        nwritten = write_file(req,fsp,data,startpos,numtowrite);
 
-       set_filetime(conn, fsp->fsp_name, mtime);
-  
+       set_close_write_time(fsp, mtime);
+
        /*
         * More insanity. W2K only closes the file if writelen > 0.
         * JRA.
@@ -6078,7 +6089,7 @@ NTSTATUS copy_file(TALLOC_CTX *ctx,
        close_file(fsp1,NORMAL_CLOSE);
 
        /* Ensure the modtime is set correctly on the destination file. */
-       fsp_set_pending_modtime( fsp2, get_mtimespec(&src_sbuf));
+       set_close_write_time(fsp2, get_mtimespec(&src_sbuf));
 
        /*
         * As we are opening fsp1 read-only we only expect
@@ -6961,6 +6972,8 @@ void reply_setattrE(struct smb_request *req)
        connection_struct *conn = req->conn;
        struct timespec ts[2];
        files_struct *fsp;
+       SMB_STRUCT_STAT sbuf;
+       NTSTATUS status;
 
        START_PROFILE(SMBsetattrE);
 
@@ -6996,23 +7009,27 @@ void reply_setattrE(struct smb_request *req)
         * Sometimes times are sent as zero - ignore them.
         */
 
-       if (null_timespec(ts[0]) && null_timespec(ts[1])) {
-               /* Ignore request */
-               if( DEBUGLVL( 3 ) ) {
-                       dbgtext( "reply_setattrE fnum=%d ", fsp->fnum);
-                       dbgtext( "ignoring zero request - not setting timestamps of 0\n" );
+       /* Ensure we have a valid stat struct for the source. */
+       if (fsp->fh->fd != -1) {
+               if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
+                       status = map_nt_error_from_unix(errno);
+                       reply_nterror(req, status);
+                       END_PROFILE(SMBsetattrE);
+                       return;
+               }
+       } else {
+               if (SMB_VFS_STAT(conn, fsp->fsp_name, &sbuf) == -1) {
+                       status = map_nt_error_from_unix(errno);
+                       reply_nterror(req, status)
+                       END_PROFILE(SMBsetattrE);
+                       return;
                }
-               END_PROFILE(SMBsetattrE);
-               return;
-       } else if (!null_timespec(ts[0]) && null_timespec(ts[1])) {
-               /* set modify time = to access time if modify time was unset */
-               ts[1] = ts[0];
        }
 
-       /* Set the date on this file */
-       /* Should we set pending modtime here ? JRA */
-       if(file_ntimes(conn, fsp->fsp_name, ts)) {
-               reply_doserror(req, ERRDOS, ERRnoaccess);
+       status = smb_set_file_time(conn, fsp, fsp->fsp_name,
+                                  &sbuf, ts, true);
+       if (!NT_STATUS_IS_OK(status)) {
+               reply_doserror(req, ERRDOS, ERRnoaccess)
                END_PROFILE(SMBsetattrE);
                return;
        }
index 897ed3343151fa95d98302b2356f339c258e1a3f..a9e7f5e34c836b824a49dd8abf0d3318a135b7ad 100644 (file)
@@ -1238,6 +1238,7 @@ static bool get_lanman2_dir_entry(TALLOC_CTX *ctx,
        bool needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/');
        bool check_mangled_names = lp_manglednames(conn->params);
        char mangled_name[13]; /* mangled 8.3 name. */
+       struct timespec write_time_ts;
 
        *out_of_space = False;
        *got_exact_match = False;
@@ -1397,6 +1398,12 @@ static bool get_lanman2_dir_entry(TALLOC_CTX *ctx,
                        adate_ts = get_atimespec(&sbuf);
                        create_date_ts = get_create_timespec(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
 
+                       write_time_ts = get_write_time(
+                               vfs_file_id_from_sbuf(conn, &sbuf));
+                       if (!null_timespec(write_time_ts)) {
+                               mdate_ts = write_time_ts;
+                       }
+
                        if (lp_dos_filetime_resolution(SNUM(conn))) {
                                dos_filetime_timespec(&create_date_ts);
                                dos_filetime_timespec(&mdate_ts);
@@ -3784,6 +3791,7 @@ static void call_trans2qfilepathinfo(connection_struct *conn,
        int len;
        time_t create_time, mtime, atime;
        struct timespec create_time_ts, mtime_ts, atime_ts;
+       struct timespec write_time_ts;
        files_struct *fsp = NULL;
        struct file_id fileid;
        struct ea_list *ea_list = NULL;
@@ -3797,6 +3805,7 @@ static void call_trans2qfilepathinfo(connection_struct *conn,
        }
 
        ZERO_STRUCT(sbuf);
+       ZERO_STRUCT(write_time_ts);
 
        if (tran_call == TRANSACT2_QFILEINFO) {
                if (total_params < 4) {
@@ -3862,6 +3871,7 @@ static void call_trans2qfilepathinfo(connection_struct *conn,
 
                        fileid = vfs_file_id_from_sbuf(conn, &sbuf);
                        delete_pending = get_delete_on_close_flag(fileid);
+                       write_time_ts = get_write_time(fileid);
                } else {
                        /*
                         * Original code - this is an open file.
@@ -3878,6 +3888,7 @@ static void call_trans2qfilepathinfo(connection_struct *conn,
                        pos = fsp->fh->position_information;
                        fileid = vfs_file_id_from_sbuf(conn, &sbuf);
                        delete_pending = get_delete_on_close_flag(fileid);
+                       write_time_ts = get_write_time(fileid);
                        access_mask = fsp->access_mask;
                }
 
@@ -3953,6 +3964,7 @@ static void call_trans2qfilepathinfo(connection_struct *conn,
                        reply_nterror(req, NT_STATUS_DELETE_PENDING);
                        return;
                }
+               write_time_ts = get_write_time(fileid);
        }
 
        if (INFO_LEVEL_IS_UNIX(info_level) && !lp_unix_extensions()) {
@@ -4073,25 +4085,20 @@ total_data=%u (should be %u)\n", (unsigned int)total_data, (unsigned int)IVAL(pd
 
        allocation_size = get_allocation_size(conn,fsp,&sbuf);
 
-       if (fsp) {
-               if (!null_timespec(fsp->pending_modtime)) {
-                       /* the pending modtime overrides the current modtime */
-                       mtime_ts = fsp->pending_modtime;
-               }
-       } else {
-               files_struct *fsp1;
+       if (!fsp) {
                /* Do we have this path open ? */
+               files_struct *fsp1;
                fileid = vfs_file_id_from_sbuf(conn, &sbuf);
                fsp1 = file_find_di_first(fileid);
-               if (fsp1 && !null_timespec(fsp1->pending_modtime)) {
-                       /* the pending modtime overrides the current modtime */
-                       mtime_ts = fsp1->pending_modtime;
-               }
                if (fsp1 && fsp1->initial_allocation_size) {
                        allocation_size = get_allocation_size(conn, fsp1, &sbuf);
                }
        }
 
+       if (!null_timespec(write_time_ts)) {
+               mtime_ts = write_time_ts;
+       }
+
        if (lp_dos_filetime_resolution(SNUM(conn))) {
                dos_filetime_timespec(&create_time_ts);
                dos_filetime_timespec(&mtime_ts);
@@ -4781,12 +4788,12 @@ NTSTATUS hardlink_internals(TALLOC_CTX *ctx,
  Deal with setting the time from any of the setfilepathinfo functions.
 ****************************************************************************/
 
-static NTSTATUS smb_set_file_time(connection_struct *conn,
-                               files_struct *fsp,
-                               const char *fname,
-                               const SMB_STRUCT_STAT *psbuf,
-                               struct timespec ts[2],
-                               bool setting_write_time)
+NTSTATUS smb_set_file_time(connection_struct *conn,
+                          files_struct *fsp,
+                          const char *fname,
+                          const SMB_STRUCT_STAT *psbuf,
+                          struct timespec ts[2],
+                          bool setting_write_time)
 {
        uint32 action =
                FILE_NOTIFY_CHANGE_LAST_ACCESS
@@ -4828,7 +4835,7 @@ static NTSTATUS smb_set_file_time(connection_struct *conn,
                }
        }
 
-       if(fsp != NULL) {
+       if (setting_write_time) {
                /*
                 * This was a setfileinfo on an open file.
                 * NT does this a lot. We also need to 
@@ -4839,13 +4846,18 @@ static NTSTATUS smb_set_file_time(connection_struct *conn,
                 * away and will set it on file close and after a write. JRA.
                 */
 
-               if (!null_timespec(ts[1])) {
-                       DEBUG(10,("smb_set_file_time: setting pending modtime to %s\n",
-                               time_to_asc(convert_timespec_to_time_t(ts[1])) ));
-                       fsp_set_pending_modtime(fsp, ts[1]);
-               }
+               DEBUG(10,("smb_set_file_time: setting pending modtime to %s\n",
+                         time_to_asc(convert_timespec_to_time_t(ts[1])) ));
 
+               if (fsp != NULL) {
+                       set_write_time_fsp(fsp, ts[1], true);
+               } else {
+                       set_write_time_path(conn, fname,
+                                           vfs_file_id_from_sbuf(conn, psbuf),
+                                           ts[1], true);
+               }
        }
+
        DEBUG(10,("smb_set_file_time: setting utimes to modified values.\n"));
 
        if(file_ntimes(conn, fname, ts)!=0) {
@@ -5670,14 +5682,11 @@ static NTSTATUS smb_set_file_allocation_info(connection_struct *conn,
                        }
                }
                /* But always update the time. */
-               if (null_timespec(fsp->pending_modtime)) {
-                       /*
-                        * This is equivalent to a write. Ensure it's seen immediately
-                        * if there are no pending writes.
-                        */
-                       set_filetime(fsp->conn, fsp->fsp_name,
-                                       timespec_current());
-               }
+               /*
+                * This is equivalent to a write. Ensure it's seen immediately
+                * if there are no pending writes.
+                */
+               trigger_write_time_update(fsp);
                return NT_STATUS_OK;
        }
 
@@ -5707,10 +5716,11 @@ static NTSTATUS smb_set_file_allocation_info(connection_struct *conn,
        }
 
        /* Changing the allocation size should set the last mod time. */
-       /* Don't need to call set_filetime as this will be flushed on
-        * close. */
-
-       fsp_set_pending_modtime(new_fsp, timespec_current());
+       /*
+        * This is equivalent to a write. Ensure it's seen immediately
+        * if there are no pending writes.
+        */
+       trigger_write_time_update(new_fsp);
 
        close_file(new_fsp,NORMAL_CLOSE);
        return NT_STATUS_OK;
@@ -6658,11 +6668,6 @@ static void call_trans2setfilepathinfo(connection_struct *conn,
 
        SSVAL(params,0,0);
 
-       if (fsp && !null_timespec(fsp->pending_modtime)) {
-               /* the pending modtime overrides the current modtime */
-               set_mtimespec(&sbuf, fsp->pending_modtime);
-       }
-
        switch (info_level) {
 
                case SMB_INFO_STANDARD: