s3:smbd: remove old comment about scavenger timer from vfs_default_durable_reconnect()
[obnox/samba/samba-obnox.git] / source3 / smbd / durable.c
index 42ad18e5b6578d5b85002ccfe0c82434803866b5..9b05d48680e7894c238911df27635ae486fac6e2 100644 (file)
@@ -99,6 +99,29 @@ NTSTATUS vfs_default_durable_cookie(struct files_struct *fsp,
        cookie.base_name = fsp->fsp_name->base_name;
        cookie.initial_allocation_size = fsp->initial_allocation_size;
        cookie.position_information = fsp->fh->position_information;
+       cookie.update_write_time_triggered = fsp->update_write_time_triggered;
+       cookie.update_write_time_on_close = fsp->update_write_time_on_close;
+       cookie.write_time_forced = fsp->write_time_forced;
+       cookie.close_write_time = fsp->close_write_time;
+
+       cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
+       cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
+       cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
+       cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
+       cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
+       cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
+       cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
+       cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
+       cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
+       cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
+       cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
+       cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
+       cookie.stat_info.st_ex_calculated_birthtime = fsp->fsp_name->st.st_ex_calculated_birthtime;
+       cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
+       cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
+       cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
+       cookie.stat_info.st_ex_mask = fsp->fsp_name->st.st_ex_mask;
+       cookie.stat_info.vfs_private = fsp->fsp_name->st.vfs_private;
 
        ndr_err = ndr_push_struct_blob(cookie_blob, mem_ctx, &cookie,
                        (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
@@ -150,10 +173,6 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
                return NT_STATUS_NOT_SUPPORTED;
        }
 
-       if (fsp->num_pending_break_messages > 0) {
-               return NT_STATUS_NOT_SUPPORTED;
-       }
-
        /*
         * For now let it be simple and do not keep
         * delete on close files durable open
@@ -173,12 +192,39 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
                return NT_STATUS_NOT_SUPPORTED;
        }
 
+       /* Ensure any pending write time updates are done. */
+       if (fsp->update_write_time_event) {
+               update_write_time_handler(fsp->conn->sconn->ev_ctx,
+                                       fsp->update_write_time_event,
+                                       timeval_current(),
+                                       (void *)fsp);
+       }
+
        /*
         * The above checks are done in mark_share_mode_disconnected() too
         * but we want to avoid getting the lock if possible
         */
        lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
        if (lck != NULL) {
+               struct smb_file_time ft;
+
+               ZERO_STRUCT(ft);
+
+               if (fsp->write_time_forced) {
+                       ft.mtime = lck->data->changed_write_time;
+               } else if (fsp->update_write_time_on_close) {
+                       if (null_timespec(fsp->close_write_time)) {
+                               ft.mtime = timespec_current();
+                       } else {
+                               ft.mtime = fsp->close_write_time;
+                       }
+               }
+
+               if (!null_timespec(ft.mtime)) {
+                       round_timespec(conn->ts_res, &ft.mtime);
+                       file_ntimes(conn, fsp->fsp_name, &ft);
+               }
+
                ok = mark_share_mode_disconnected(lck, fsp);
                if (!ok) {
                        TALLOC_FREE(lck);
@@ -195,6 +241,11 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
        }
        TALLOC_FREE(lck);
 
+       status = vfs_stat_fsp(fsp);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
        ZERO_STRUCT(cookie);
        cookie.allow_reconnect = true;
        cookie.id = fsp->file_id;
@@ -202,6 +253,29 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
        cookie.base_name = fsp->fsp_name->base_name;
        cookie.initial_allocation_size = fsp->initial_allocation_size;
        cookie.position_information = fsp->fh->position_information;
+       cookie.update_write_time_triggered = fsp->update_write_time_triggered;
+       cookie.update_write_time_on_close = fsp->update_write_time_on_close;
+       cookie.write_time_forced = fsp->write_time_forced;
+       cookie.close_write_time = fsp->close_write_time;
+
+       cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
+       cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
+       cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
+       cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
+       cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
+       cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
+       cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
+       cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
+       cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
+       cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
+       cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
+       cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
+       cookie.stat_info.st_ex_calculated_birthtime = fsp->fsp_name->st.st_ex_calculated_birthtime;
+       cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
+       cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
+       cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
+       cookie.stat_info.st_ex_mask = fsp->fsp_name->st.st_ex_mask;
+       cookie.stat_info.vfs_private = fsp->fsp_name->st.vfs_private;
 
        ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie,
                        (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
@@ -220,6 +294,263 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
        return NT_STATUS_OK;
 }
 
+
+/**
+ * Check whether a cookie-stored struct info is the same
+ * as a given SMB_STRUCT_STAT, as coming with the fsp.
+ */
+static bool vfs_default_durable_reconnect_check_stat(
+                               struct vfs_default_durable_stat *cookie_st,
+                               SMB_STRUCT_STAT *fsp_st,
+                               const char *name)
+{
+       int ret;
+
+       if (cookie_st->st_ex_dev != fsp_st->st_ex_dev) {
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:%llu != stat:%llu, "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_dev",
+                         (unsigned long long)cookie_st->st_ex_dev,
+                         (unsigned long long)fsp_st->st_ex_dev));
+               return false;
+       }
+
+       if (cookie_st->st_ex_ino != fsp_st->st_ex_ino) {
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:%llu != stat:%llu, "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_ino",
+                         (unsigned long long)cookie_st->st_ex_ino,
+                         (unsigned long long)fsp_st->st_ex_ino));
+               return false;
+       }
+
+       if (cookie_st->st_ex_mode != fsp_st->st_ex_mode) {
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:%llu != stat:%llu, "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_mode",
+                         (unsigned long long)cookie_st->st_ex_mode,
+                         (unsigned long long)fsp_st->st_ex_mode));
+               return false;
+       }
+
+       if (cookie_st->st_ex_nlink != fsp_st->st_ex_nlink) {
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:%llu != stat:%llu, "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_nlink",
+                         (unsigned long long)cookie_st->st_ex_nlink,
+                         (unsigned long long)fsp_st->st_ex_nlink));
+               return false;
+       }
+
+       if (cookie_st->st_ex_uid != fsp_st->st_ex_uid) {
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:%llu != stat:%llu, "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_uid",
+                         (unsigned long long)cookie_st->st_ex_uid,
+                         (unsigned long long)fsp_st->st_ex_uid));
+               return false;
+       }
+
+       if (cookie_st->st_ex_gid != fsp_st->st_ex_gid) {
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:%llu != stat:%llu, "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_gid",
+                         (unsigned long long)cookie_st->st_ex_gid,
+                         (unsigned long long)fsp_st->st_ex_gid));
+               return false;
+       }
+
+       if (cookie_st->st_ex_rdev != fsp_st->st_ex_rdev) {
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:%llu != stat:%llu, "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_rdev",
+                         (unsigned long long)cookie_st->st_ex_rdev,
+                         (unsigned long long)fsp_st->st_ex_rdev));
+               return false;
+       }
+
+       if (cookie_st->st_ex_size != fsp_st->st_ex_size) {
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:%llu != stat:%llu, "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_size",
+                         (unsigned long long)cookie_st->st_ex_size,
+                         (unsigned long long)fsp_st->st_ex_size));
+               return false;
+       }
+
+       ret = timespec_compare(&cookie_st->st_ex_atime,
+                              &fsp_st->st_ex_atime);
+       if (ret != 0) {
+               struct timeval tc, ts;
+               tc = convert_timespec_to_timeval(cookie_st->st_ex_atime);
+               ts = convert_timespec_to_timeval(fsp_st->st_ex_atime);
+
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:'%s' != stat:'%s', "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_atime",
+                         timeval_string(talloc_tos(), &tc, true),
+                         timeval_string(talloc_tos(), &ts, true)));
+               return false;
+       }
+
+       ret = timespec_compare(&cookie_st->st_ex_mtime,
+                              &fsp_st->st_ex_mtime);
+       if (ret != 0) {
+               struct timeval tc, ts;
+               tc = convert_timespec_to_timeval(cookie_st->st_ex_mtime);
+               ts = convert_timespec_to_timeval(fsp_st->st_ex_mtime);
+
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:'%s' != stat:'%s', "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_mtime",
+                         timeval_string(talloc_tos(), &tc, true),
+                         timeval_string(talloc_tos(), &ts, true)));
+               return false;
+       }
+
+       ret = timespec_compare(&cookie_st->st_ex_ctime,
+                              &fsp_st->st_ex_ctime);
+       if (ret != 0) {
+               struct timeval tc, ts;
+               tc = convert_timespec_to_timeval(cookie_st->st_ex_ctime);
+               ts = convert_timespec_to_timeval(fsp_st->st_ex_ctime);
+
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:'%s' != stat:'%s', "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_ctime",
+                         timeval_string(talloc_tos(), &tc, true),
+                         timeval_string(talloc_tos(), &ts, true)));
+               return false;
+       }
+
+       ret = timespec_compare(&cookie_st->st_ex_btime,
+                              &fsp_st->st_ex_btime);
+       if (ret != 0) {
+               struct timeval tc, ts;
+               tc = convert_timespec_to_timeval(cookie_st->st_ex_btime);
+               ts = convert_timespec_to_timeval(fsp_st->st_ex_btime);
+
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:'%s' != stat:'%s', "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_btime",
+                         timeval_string(talloc_tos(), &tc, true),
+                         timeval_string(talloc_tos(), &ts, true)));
+               return false;
+       }
+
+       if (cookie_st->st_ex_calculated_birthtime !=
+           fsp_st->st_ex_calculated_birthtime)
+       {
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:%llu != stat:%llu, "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_calculated_birthtime",
+                         (unsigned long long)cookie_st->st_ex_calculated_birthtime,
+                         (unsigned long long)fsp_st->st_ex_calculated_birthtime));
+               return false;
+       }
+
+       if (cookie_st->st_ex_blksize != fsp_st->st_ex_blksize) {
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:%llu != stat:%llu, "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_blksize",
+                         (unsigned long long)cookie_st->st_ex_blksize,
+                         (unsigned long long)fsp_st->st_ex_blksize));
+               return false;
+       }
+
+       if (cookie_st->st_ex_blocks != fsp_st->st_ex_blocks) {
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:%llu != stat:%llu, "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_blocks",
+                         (unsigned long long)cookie_st->st_ex_blocks,
+                         (unsigned long long)fsp_st->st_ex_blocks));
+               return false;
+       }
+
+       if (cookie_st->st_ex_flags != fsp_st->st_ex_flags) {
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:%llu != stat:%llu, "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_flags",
+                         (unsigned long long)cookie_st->st_ex_flags,
+                         (unsigned long long)fsp_st->st_ex_flags));
+               return false;
+       }
+
+       if (cookie_st->st_ex_mask != fsp_st->st_ex_mask) {
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:%llu != stat:%llu, "
+                         "denying durable reconnect\n",
+                         name,
+                         "st_ex_mask",
+                         (unsigned long long)cookie_st->st_ex_mask,
+                         (unsigned long long)fsp_st->st_ex_mask));
+               return false;
+       }
+
+       if (cookie_st->vfs_private != fsp_st->vfs_private) {
+               DEBUG(1, ("vfs_default_durable_reconnect (%s): "
+                         "stat_ex.%s differs: "
+                         "cookie:%llu != stat:%llu, "
+                         "denying durable reconnect\n",
+                         name,
+                         "vfs_private",
+                         (unsigned long long)cookie_st->vfs_private,
+                         (unsigned long long)fsp_st->vfs_private));
+               return false;
+       }
+
+       return true;
+}
+
 NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
                                       struct smb_request *smb1req,
                                       struct smbXsrv_open *op,
@@ -281,12 +612,10 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
        }
 
        /* Create an smb_filename with stream_name == NULL. */
-       status = create_synthetic_smb_fname(talloc_tos(),
-                                           cookie.base_name,
-                                           NULL, NULL,
-                                           &smb_fname);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       smb_fname = synthetic_smb_fname(talloc_tos(), cookie.base_name,
+                                       NULL, NULL);
+       if (smb_fname == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
 
        ret = SMB_VFS_LSTAT(conn, smb_fname);
@@ -366,20 +695,6 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
-       /*
-        * TODO:
-        * add scavenger timer functionality
-        *
-        * For now we always allow the reconnect
-        */
-#if 0
-       expire_time = op->global->disconnect_time;
-       expire_time += NTTIME_MAGIC(op->global->durable_timeout_msec);
-       if (expire < now) {
-               //TODO reopen and close before telling the client...
-       }
-#endif
-
        /*
         * 2. proceed with opening file
         */
@@ -406,8 +721,6 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
        /*
         * TODO:
         * Do we need to store the modified flag in the DB?
-        * How to handle update_write_time and friends
-        * during a disconnected client on a durable handle?
         */
        fsp->modified = false;
        /*
@@ -426,6 +739,10 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
 
        fsp->initial_allocation_size = cookie.initial_allocation_size;
        fsp->fh->position_information = cookie.position_information;
+       fsp->update_write_time_triggered = cookie.update_write_time_triggered;
+       fsp->update_write_time_on_close = cookie.update_write_time_on_close;
+       fsp->write_time_forced = cookie.write_time_forced;
+       fsp->close_write_time = cookie.close_write_time;
 
        status = fsp_set_smb_fname(fsp, smb_fname);
        if (!NT_STATUS_IS_OK(status)) {
@@ -477,6 +794,15 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
                return status;
        }
 
+       /*
+        * We now check the stat info stored in the cookie against
+        * the current stat data from the file we just opened.
+        * If any detail differs, we deny the durable reconnect,
+        * because in that case it is very likely that someone
+        * opened the file while the handle was disconnected,
+        * which has to be interpreted as an oplock break.
+        */
+
        ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
        if (ret == -1) {
                status = map_nt_error_from_unix_common(errno);
@@ -493,7 +819,6 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
                op->compat = NULL;
                fsp_free(fsp);
                return status;
-
        }
 
        if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
@@ -523,6 +848,22 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
+       ok = vfs_default_durable_reconnect_check_stat(&cookie.stat_info,
+                                                     &fsp->fsp_name->st,
+                                                     fsp_str_dbg(fsp));
+       if (!ok) {
+               ret = SMB_VFS_CLOSE(fsp);
+               if (ret == -1) {
+                       DEBUG(0, ("vfs_default_durable_reconnect: "
+                                 "SMB_VFS_CLOSE failed (%s) - leaking file "
+                                 "descriptor\n", strerror(errno)));
+               }
+               TALLOC_FREE(lck);
+               op->compat = NULL;
+               fsp_free(fsp);
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       }
+
        status = set_file_oplock(fsp, e->op_type);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(1, ("vfs_default_durable_reconnect failed to set oplock "