Extend NTIMES to allow setting create_time
[metze/samba/wip.git] / source3 / smbd / close.c
index 4bd23a35fc804a43756acbf93e3b865b09705aab..2fb8ec2bb5514e5398034d7daecd4f98fb7a18a7 100644 (file)
@@ -67,10 +67,20 @@ static void check_magic(struct files_struct *fsp)
                return;
        }
 
-       chmod(fsp->fsp_name,0755);
-       ret = smbrun(fsp->fsp_name,&tmp_fd);
+       /* Ensure we don't depend on user's PATH. */
+       p = talloc_asprintf(ctx, "./%s", fsp->fsp_name);
+       if (!p) {
+               TALLOC_FREE(ctx);
+               return;
+       }
+
+       if (chmod(fsp->fsp_name,0755) == -1) {
+               TALLOC_FREE(ctx);
+               return;
+       }
+       ret = smbrun(p,&tmp_fd);
        DEBUG(3,("Invoking magic command %s gave %d\n",
-               fsp->fsp_name,ret));
+               p,ret));
 
        unlink(fsp->fsp_name);
        if (ret != 0 || tmp_fd == -1) {
@@ -106,8 +116,7 @@ static void check_magic(struct files_struct *fsp)
 static NTSTATUS close_filestruct(files_struct *fsp)
 {
        NTSTATUS status = NT_STATUS_OK;
-       connection_struct *conn = fsp->conn;
-    
+
        if (fsp->fh->fd != -1) {
                if(flush_write_cache(fsp, CLOSE_FLUSH) == -1) {
                        status = map_nt_error_from_unix(errno);
@@ -115,9 +124,8 @@ static NTSTATUS close_filestruct(files_struct *fsp)
                delete_write_cache(fsp);
        }
 
-       conn->num_files_open--;
        return status;
-}    
+}
 
 /****************************************************************************
  If any deferred opens are waiting on this close, notify them.
@@ -159,7 +167,7 @@ static void notify_deferred_opens(struct share_mode_lock *lck)
  Delete all streams
 ****************************************************************************/
 
-static NTSTATUS delete_all_streams(connection_struct *conn, const char *fname)
+NTSTATUS delete_all_streams(connection_struct *conn, const char *fname)
 {
        struct stream_struct *stream_info;
        int i;
@@ -246,7 +254,8 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
         * This prevents race conditions with the file being created. JRA.
         */
 
-       lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL);
+       lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL,
+                                 NULL);
 
        if (lck == NULL) {
                DEBUG(0, ("close_remove_share_mode: Could not get share mode "
@@ -254,6 +263,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));
@@ -316,6 +329,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. */
 
@@ -427,6 +445,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 smb_file_time ft;
+       NTSTATUS status;
+
+       ZERO_STRUCT(sbuf);
+       ZERO_STRUCT(ft);
+
+       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;
+       }
+
+       ft.mtime = fsp->close_write_time;
+       status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name,
+                                  &sbuf, &ft, true);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       return NT_STATUS_OK;
+}
+
 /****************************************************************************
  Close a file.
 
@@ -435,12 +513,14 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
  delete on close is done on normal and shutdown close.
 ****************************************************************************/
 
-static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_type)
+static NTSTATUS close_normal_file(struct smb_request *req, files_struct *fsp,
+                                 enum file_close_type close_type)
 {
        NTSTATUS status = NT_STATUS_OK;
        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) {
@@ -465,7 +545,7 @@ static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_
 
        if (fsp->print_file) {
                print_fsp_end(fsp, close_type);
-               file_free(fsp);
+               file_free(req, fsp);
                return NT_STATUS_OK;
        }
 
@@ -495,11 +575,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)) {
@@ -508,15 +584,17 @@ 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;
                }
        }
 
        DEBUG(2,("%s closed file %s (numopen=%d) %s\n",
-               conn->user,fsp->fsp_name,
-               conn->num_files_open,
+               conn->server_info->unix_name,fsp->fsp_name,
+               conn->num_files_open - 1,
                nt_errstr(status) ));
 
-       file_free(fsp);
+       file_free(req, fsp);
        return status;
 }
 
@@ -524,7 +602,8 @@ static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_
  Close a directory opened by an NT SMB call. 
 ****************************************************************************/
   
-static NTSTATUS close_directory(files_struct *fsp, enum file_close_type close_type)
+static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp,
+                               enum file_close_type close_type)
 {
        struct share_mode_lock *lck = 0;
        bool delete_dir = False;
@@ -535,7 +614,8 @@ static NTSTATUS close_directory(files_struct *fsp, enum file_close_type close_ty
         * reference to a directory also.
         */
 
-       lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL);
+       lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL,
+                                 NULL);
 
        if (lck == NULL) {
                DEBUG(0, ("close_directory: Could not get share mode lock for %s\n", fsp->fsp_name));
@@ -624,49 +704,37 @@ static NTSTATUS close_directory(files_struct *fsp, enum file_close_type close_ty
                        fsp, NT_STATUS_OK);
        }
 
-       /*
-        * Do the code common to files and directories.
-        */
-       close_filestruct(fsp);
-       file_free(fsp);
-       return status;
-}
+       status = fd_close(fsp);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("Could not close dir! fname=%s, fd=%d, err=%d=%s\n",
+                         fsp->fsp_name, fsp->fh->fd, errno, strerror(errno)));
+       }
 
-/****************************************************************************
- Close a 'stat file' opened internally.
-****************************************************************************/
-  
-static NTSTATUS close_stat(files_struct *fsp)
-{
        /*
         * Do the code common to files and directories.
         */
        close_filestruct(fsp);
-       file_free(fsp);
-       return NT_STATUS_OK;
+       file_free(req, fsp);
+       return status;
 }
 
 /****************************************************************************
  Close a files_struct.
 ****************************************************************************/
   
-NTSTATUS close_file(files_struct *fsp, enum file_close_type close_type)
+NTSTATUS close_file(struct smb_request *req, files_struct *fsp,
+                   enum file_close_type close_type)
 {
        NTSTATUS status;
        struct files_struct *base_fsp = fsp->base_fsp;
 
        if(fsp->is_directory) {
-               status = close_directory(fsp, close_type);
-       } else if (fsp->is_stat) {
-               status = close_stat(fsp);
+               status = close_directory(req, fsp, close_type);
        } else if (fsp->fake_file_handle != NULL) {
-               status = close_fake_file(fsp);
+               status = close_fake_file(req, fsp);
        } else {
-               status = close_normal_file(fsp, close_type);
-       }
-
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+               status = close_normal_file(req, fsp, close_type);
        }
 
        if ((base_fsp != NULL) && (close_type != SHUTDOWN_CLOSE)) {
@@ -681,8 +749,42 @@ NTSTATUS close_file(files_struct *fsp, enum file_close_type close_type)
                 */
 
                SMB_ASSERT(base_fsp->base_fsp == NULL);
-               close_file(base_fsp, close_type);
+               close_file(req, base_fsp, close_type);
        }
 
        return status;
 }
+
+/****************************************************************************
+ Deal with an (authorized) message to close a file given the share mode
+ entry.
+****************************************************************************/
+
+void msg_close_file(struct messaging_context *msg_ctx,
+                       void *private_data,
+                       uint32_t msg_type,
+                       struct server_id server_id,
+                       DATA_BLOB *data)
+{
+       files_struct *fsp = NULL;
+       struct share_mode_entry e;
+
+       message_to_share_mode_entry(&e, (char *)data->data);
+
+       if(DEBUGLVL(10)) {
+               char *sm_str = share_mode_str(NULL, 0, &e);
+               if (!sm_str) {
+                       smb_panic("talloc failed");
+               }
+               DEBUG(10,("msg_close_file: got request to close share mode "
+                       "entry %s\n", sm_str));
+               TALLOC_FREE(sm_str);
+       }
+
+       fsp = file_find_dif(e.id, e.share_file_id);
+       if (!fsp) {
+               DEBUG(10,("msg_close_file: failed to find file.\n"));
+               return;
+       }
+       close_file(NULL, fsp, NORMAL_CLOSE);
+}