s3:smbd: add support for SMB2 Keepalive (SMB2 Echo)
[ira/wip.git] / source3 / smbd / fileio.c
index 8cea4989f5b4dd16f5d3ad26208e951094a1e908..adf664b3960301da1713a81589b8b8e75a6f9307 100644 (file)
@@ -20,6 +20,7 @@
 */
 
 #include "includes.h"
+#include "smbd/globals.h"
 
 static bool setup_write_cache(files_struct *, SMB_OFF_T);
 
@@ -109,9 +110,6 @@ tryagain:
        return(ret);
 }
 
-/* how many write cache buffers have been allocated */
-static unsigned int allocated_write_caches;
-
 /****************************************************************************
  *Really* write to a file.
 ****************************************************************************/
@@ -142,27 +140,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 +169,78 @@ 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,
+                                     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 someone else */
+       update_write_time(fsp);
+}
+
+/*********************************************************
+ Schedule a write time update for WRITE_TIME_UPDATE_USEC_DELAY
+ in the future.
+*********************************************************/
+
+void trigger_write_time_update(struct files_struct *fsp)
+{
+       int delay;
+
+       if (fsp->write_time_forced) {
+               /* No point - "sticky" write times
+                * in effect.
+                */
+               return;
+       }
+
+       if (fsp->update_write_time_triggered) {
+               /*
+                * We only update the write time
+                * on the first write. After that
+                * no other writes affect this.
+                */
+               return;
+       }
+       fsp->update_write_time_triggered = true;
+
+       delay = lp_parm_int(SNUM(fsp->conn),
+                           "smbd", "writetimeupdatedelay",
+                           WRITE_TIME_UPDATE_USEC_DELAY);
+
+       /* 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(0, delay),
+                               update_write_time_handler, fsp);
+}
+
+void trigger_write_time_update_immediate(struct files_struct *fsp)
+{
+        if (fsp->write_time_forced) {
+               /*
+                * No point - "sticky" write times
+                * in effect.
+                */
+                return;
+        }
+
+       TALLOC_FREE(fsp->update_write_time_event);
+       DEBUG(5, ("Update write time immediate on %s\n", fsp->fsp_name));
+
+       fsp->update_write_time_triggered = true;
+
+        fsp->update_write_time_on_close = false;
+       update_write_time(fsp);
+}
+
 /****************************************************************************
  Write to a file.
 ****************************************************************************/
@@ -207,10 +256,9 @@ ssize_t write_file(struct smb_request *req,
        int write_path = -1;
 
        if (fsp->print_file) {
-               fstring sharename;
                uint32 jobid;
 
-               if (!rap_to_pjobid(fsp->rap_print_jobid, sharename, &jobid)) {
+               if (!rap_to_pjobid(fsp->rap_print_jobid, NULL, &jobid)) {
                        DEBUG(3,("write_file: Unable to map RAP jobid %u to jobid.\n",
                                                (unsigned int)fsp->rap_print_jobid ));
                        errno = EBADF;
@@ -230,7 +278,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)) {
@@ -267,7 +317,9 @@ ssize_t write_file(struct smb_request *req,
         * the shared memory area whilst doing this.
         */
 
-       release_level_2_oplocks_on_change(fsp);
+       /* This should actually be improved to span the write. */
+       contend_level2_oplocks_begin(fsp, LEVEL2_CONTEND_WRITE);
+       contend_level2_oplocks_end(fsp, LEVEL2_CONTEND_WRITE);
 
 #ifdef WITH_PROFILE
        if (profile_p && profile_p->writecache_total_writes % 500 == 0) {
@@ -813,11 +865,14 @@ void set_filelen_write_cache(files_struct *fsp, SMB_OFF_T file_size)
                /* The cache *must* have been flushed before we do this. */
                if (fsp->wcp->data_size != 0) {
                        char *msg;
-                       asprintf(&msg, "set_filelen_write_cache: size change "
+                       if (asprintf(&msg, "set_filelen_write_cache: size change "
                                 "on file %s with write cache size = %lu\n",
                                 fsp->fsp_name,
-                                (unsigned long)fsp->wcp->data_size);
-                       smb_panic(msg);
+                                (unsigned long)fsp->wcp->data_size) != -1) {
+                               smb_panic(msg);
+                       } else {
+                               smb_panic("set_filelen_write_cache");
+                       }
                }
                fsp->wcp->file_size = file_size;
        }