r21108: Send sys_notify_watch through the VFS, FAM is next
[ira/wip.git] / source3 / smbd / close.c
index bc2d6c3507ad6ddaecd18c05e41dd74188348e54..714e0615de690e61be68883ee074902d2b6afd85 100644 (file)
@@ -22,6 +22,8 @@
 
 #include "includes.h"
 
+extern struct current_user current_user;
+
 /****************************************************************************
  Run a file if it is a magic script.
 ****************************************************************************/
@@ -144,12 +146,14 @@ static void notify_deferred_opens(struct share_mode_lock *lck)
  Deal with removing a share mode on last close.
 ****************************************************************************/
 
-static int close_remove_share_mode(files_struct *fsp, enum file_close_type close_type)
+static NTSTATUS close_remove_share_mode(files_struct *fsp,
+                                       enum file_close_type close_type)
 {
        connection_struct *conn = fsp->conn;
        BOOL delete_file = False;
        struct share_mode_lock *lck;
        SMB_STRUCT_STAT sbuf;
+       NTSTATUS status = NT_STATUS_OK;
 
        /*
         * Lock the share entries, and determine if we should delete
@@ -162,7 +166,7 @@ static int close_remove_share_mode(files_struct *fsp, enum file_close_type close
        if (lck == NULL) {
                DEBUG(0, ("close_remove_share_mode: Could not get share mode "
                          "lock for file %s\n", fsp->fsp_name));
-               return EINVAL;
+               return NT_STATUS_INVALID_PARAMETER;
        }
 
        if (!del_share_mode(lck, fsp)) {
@@ -170,7 +174,23 @@ static int close_remove_share_mode(files_struct *fsp, enum file_close_type close
                          "entry for file %s\n", fsp->fsp_name));
        }
 
-       delete_file = (lck->delete_on_close | lck->initial_delete_on_close);
+       if (fsp->initial_delete_on_close && (lck->delete_token == NULL)) {
+               BOOL became_user = False;
+
+               /* Initial delete on close was set and no one else
+                * wrote a real delete on close. */
+
+               if (current_user.vuid != fsp->vuid) {
+                       become_user(conn, fsp->vuid);
+                       became_user = True;
+               }
+               set_delete_on_close_lck(lck, True, &current_user.ut);
+               if (became_user) {
+                       unbecome_user();
+               }
+       }
+
+       delete_file = lck->delete_on_close;
 
        if (delete_file) {
                int i;
@@ -197,7 +217,7 @@ static int close_remove_share_mode(files_struct *fsp, enum file_close_type close
            || !delete_file
            || (lck->delete_token == NULL)) {
                TALLOC_FREE(lck);
-               return 0;
+               return NT_STATUS_OK;
        }
 
        /*
@@ -227,6 +247,9 @@ static int close_remove_share_mode(files_struct *fsp, enum file_close_type close
                DEBUG(5,("close_remove_share_mode: file %s. Delete on close "
                         "was set and stat failed with error %s\n",
                         fsp->fsp_name, strerror(errno) ));
+               /*
+                * Don't save the errno here, we ignore this error
+                */
                goto done;
        }
 
@@ -239,6 +262,9 @@ static int close_remove_share_mode(files_struct *fsp, enum file_close_type close
                         fsp->fsp_name,
                         (unsigned int)fsp->dev, (double)fsp->inode,
                         (unsigned int)sbuf.st_dev, (double)sbuf.st_ino ));
+               /*
+                * Don't save the errno here, we ignore this error
+                */
                goto done;
        }
 
@@ -254,17 +280,19 @@ static int close_remove_share_mode(files_struct *fsp, enum file_close_type close
                DEBUG(5,("close_remove_share_mode: file %s. Delete on close "
                         "was set and unlink failed with error %s\n",
                         fsp->fsp_name, strerror(errno) ));
+
+               status = map_nt_error_from_unix(errno);
                goto done;
        }
 
+       status = NT_STATUS_FILE_DELETED;
+
  done:
        /* unbecome user. */
        pop_sec_ctx();
        
-       process_pending_change_notify_queue((time_t)0);
-
        TALLOC_FREE(lck);
-       return 0;
+       return status;
 }
 
 /****************************************************************************
@@ -390,7 +418,37 @@ static int close_directory(files_struct *fsp, enum file_close_type close_type)
                DEBUG(0, ("close_directory: Could not delete share entry for %s\n", fsp->fsp_name));
        }
 
-       delete_dir = (lck->delete_on_close | lck->initial_delete_on_close);
+       if (fsp->initial_delete_on_close) {
+               BOOL became_user = False;
+
+               /* Initial delete on close was set - for
+                * directories we don't care if anyone else
+                * wrote a real delete on close. */
+
+               if (current_user.vuid != fsp->vuid) {
+                       become_user(fsp->conn, fsp->vuid);
+                       became_user = True;
+               }
+               send_stat_cache_delete_message(fsp->fsp_name);
+               set_delete_on_close_lck(lck, True, &current_user.ut);
+               if (became_user) {
+                       unbecome_user();
+               }
+       }
+
+       delete_dir = lck->delete_on_close;
+
+       if (delete_dir) {
+               int i;
+               /* See if others still have the dir open. If this is the
+                * case, then don't delete */
+               for (i=0; i<lck->num_share_modes; i++) {
+                       if (is_valid_share_mode_entry(&lck->share_modes[i])) {
+                               delete_dir = False;
+                               break;
+                       }
+               }
+       }
 
        if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) &&
                                delete_dir &&
@@ -426,10 +484,7 @@ static int close_directory(files_struct *fsp, enum file_close_type close_type)
 
                if(ok) {
                        remove_pending_change_notify_requests_by_fid(fsp, NT_STATUS_DELETE_PENDING);
-                       remove_pending_change_notify_requests_by_filename(fsp, NT_STATUS_DELETE_PENDING);
-
                }
-               process_pending_change_notify_queue((time_t)0);
        } else {
                TALLOC_FREE(lck);
                remove_pending_change_notify_requests_by_fid(