[GLUE] Rsync SAMBA_3_2_0 SVN r25598 in order to create the v3-2-test branch.
[samba.git] / source / smbd / close.c
index bc2d6c3507ad6ddaecd18c05e41dd74188348e54..ae45aaa6dac81e7bd6adf601f83c6fa8c35d4736 100644 (file)
-/* 
+/*
    Unix SMB/CIFS implementation.
    file closing
    Copyright (C) Andrew Tridgell 1992-1998
-   Copyright (C) Jeremy Allison 1992-2004.
+   Copyright (C) Jeremy Allison 1992-2007.
    Copyright (C) Volker Lendecke 2005
-   
+
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
-   
+
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
-   
+
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 
+extern struct current_user current_user;
+
 /****************************************************************************
  Run a file if it is a magic script.
 ****************************************************************************/
 
 static void check_magic(files_struct *fsp,connection_struct *conn)
 {
-       if (!*lp_magicscript(SNUM(conn)))
+       int ret;
+       const char *magic_output = NULL;
+       SMB_STRUCT_STAT st;
+       int tmp_fd, outfd;
+       TALLOC_CTX *ctx = NULL;
+       const char *p;
+
+       if (!*lp_magicscript(SNUM(conn))) {
                return;
+       }
 
        DEBUG(5,("checking magic for %s\n",fsp->fsp_name));
 
-       {
-               char *p;
-               if (!(p = strrchr_m(fsp->fsp_name,'/')))
-                       p = fsp->fsp_name;
-               else
-                       p++;
-
-               if (!strequal(lp_magicscript(SNUM(conn)),p))
-                       return;
-       }
-
-       {
-               int ret;
-               pstring magic_output;
-               pstring fname;
-               SMB_STRUCT_STAT st;
-               int tmp_fd, outfd;
-
-               pstrcpy(fname,fsp->fsp_name);
-               if (*lp_magicoutput(SNUM(conn)))
-                       pstrcpy(magic_output,lp_magicoutput(SNUM(conn)));
-               else
-                       slprintf(magic_output,sizeof(fname)-1, "%s.out",fname);
-
-               chmod(fname,0755);
-               ret = smbrun(fname,&tmp_fd);
-               DEBUG(3,("Invoking magic command %s gave %d\n",fname,ret));
-               unlink(fname);
-               if (ret != 0 || tmp_fd == -1) {
-                       if (tmp_fd != -1)
-                               close(tmp_fd);
-                       return;
-               }
-               outfd = open(magic_output, O_CREAT|O_EXCL|O_RDWR, 0600);
-               if (outfd == -1) {
-                       close(tmp_fd);
-                       return;
-               }
+       if (!(p = strrchr_m(fsp->fsp_name,'/'))) {
+               p = fsp->fsp_name;
+       } else {
+               p++;
+       }
+
+       if (!strequal(lp_magicscript(SNUM(conn)),p)) {
+               return;
+       }
+
+       ctx = talloc_stackframe();
+
+       if (*lp_magicoutput(SNUM(conn))) {
+               magic_output = lp_magicoutput(SNUM(conn));
+       } else {
+               magic_output = talloc_asprintf(ctx,
+                               "%s.out",
+                               fsp->fsp_name);
+       }
+       if (!magic_output) {
+               TALLOC_FREE(ctx);
+               return;
+       }
+
+       chmod(fsp->fsp_name,0755);
+       ret = smbrun(fsp->fsp_name,&tmp_fd);
+       DEBUG(3,("Invoking magic command %s gave %d\n",
+               fsp->fsp_name,ret));
 
-               if (sys_fstat(tmp_fd,&st) == -1) {
+       unlink(fsp->fsp_name);
+       if (ret != 0 || tmp_fd == -1) {
+               if (tmp_fd != -1) {
                        close(tmp_fd);
-                       close(outfd);
-                       return;
                }
+               TALLOC_FREE(ctx);
+               return;
+       }
+       outfd = open(magic_output, O_CREAT|O_EXCL|O_RDWR, 0600);
+       if (outfd == -1) {
+               close(tmp_fd);
+               TALLOC_FREE(ctx);
+               return;
+       }
 
-               transfer_file(tmp_fd,outfd,(SMB_OFF_T)st.st_size);
+       if (sys_fstat(tmp_fd,&st) == -1) {
                close(tmp_fd);
                close(outfd);
+               return;
        }
+
+       transfer_file(tmp_fd,outfd,(SMB_OFF_T)st.st_size);
+       close(tmp_fd);
+       close(outfd);
+       TALLOC_FREE(ctx);
 }
 
 /****************************************************************************
   Common code to close a file or a directory.
 ****************************************************************************/
 
-static int close_filestruct(files_struct *fsp)
-{   
+static NTSTATUS close_filestruct(files_struct *fsp)
+{
+       NTSTATUS status = NT_STATUS_OK;
        connection_struct *conn = fsp->conn;
-       int ret = 0;
     
        if (fsp->fh->fd != -1) {
-               if(flush_write_cache(fsp, CLOSE_FLUSH) == -1)
-                       ret = -1;
-
+               if(flush_write_cache(fsp, CLOSE_FLUSH) == -1) {
+                       status = map_nt_error_from_unix(errno);
+               }
                delete_write_cache(fsp);
        }
 
        conn->num_files_open--;
-       SAFE_FREE(fsp->wbmpx_ptr);
-
-       return ret;
+       return status;
 }    
 
 /****************************************************************************
@@ -134,8 +146,10 @@ static void notify_deferred_opens(struct share_mode_lock *lck)
 
                        share_mode_entry_to_message(msg, e);
 
-                       message_send_pid(e->pid, MSG_SMB_OPEN_RETRY,
-                                        msg, MSG_SMB_SHARE_MODE_ENTRY_SIZE, True);
+                       messaging_send_buf(smbd_messaging_context(),
+                                          e->pid, MSG_SMB_OPEN_RETRY,
+                                          (uint8 *)msg,
+                                          MSG_SMB_SHARE_MODE_ENTRY_SIZE);
                }
        }
 }
@@ -144,12 +158,16 @@ 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;
+       int ret;
+       struct file_id id;
 
        /*
         * Lock the share entries, and determine if we should delete
@@ -157,12 +175,12 @@ static int close_remove_share_mode(files_struct *fsp, enum file_close_type close
         * This prevents race conditions with the file being created. JRA.
         */
 
-       lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, NULL, NULL);
+       lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL);
 
        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,14 +188,34 @@ 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;
                /* See if others still have the file open. If this is the
-                * case, then don't delete */
+                * case, then don't delete. If all opens are POSIX delete now. */
                for (i=0; i<lck->num_share_modes; i++) {
-                       if (is_valid_share_mode_entry(&lck->share_modes[i])) {
+                       struct share_mode_entry *e = &lck->share_modes[i];
+                       if (is_valid_share_mode_entry(e)) {
+                               if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) {
+                                       continue;
+                               }
                                delete_file = False;
                                break;
                        }
@@ -197,7 +235,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;
        }
 
        /*
@@ -222,23 +260,37 @@ static int close_remove_share_mode(files_struct *fsp, enum file_close_type close
 
        /* We can only delete the file if the name we have is still valid and
           hasn't been renamed. */
-       
-       if(SMB_VFS_STAT(conn,fsp->fsp_name,&sbuf) != 0) {
+
+       if (fsp->posix_open) {
+               ret = SMB_VFS_LSTAT(conn,fsp->fsp_name,&sbuf);
+       } else {
+               ret = SMB_VFS_STAT(conn,fsp->fsp_name,&sbuf);
+       }
+
+       if (ret != 0) {
                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;
        }
 
-       if(sbuf.st_dev != fsp->dev || sbuf.st_ino != fsp->inode) {
+       id = vfs_file_id_from_sbuf(conn, &sbuf);
+
+       if (!file_id_equal(&fsp->file_id, &id)) {
                DEBUG(5,("close_remove_share_mode: file %s. Delete on close "
                         "was set and dev and/or inode does not match\n",
                         fsp->fsp_name ));
-               DEBUG(5,("close_remove_share_mode: file %s. stored dev = %x, "
-                        "inode = %.0f stat dev = %x, inode = %.0f\n",
+               DEBUG(5,("close_remove_share_mode: file %s. stored file_id %s, "
+                        "stat file_id %s\n",
                         fsp->fsp_name,
-                        (unsigned int)fsp->dev, (double)fsp->inode,
-                        (unsigned int)sbuf.st_dev, (double)sbuf.st_ino ));
+                        file_id_string_tos(&fsp->file_id),
+                        file_id_string_tos(&id)));
+               /*
+                * Don't save the errno here, we ignore this error
+                */
                goto done;
        }
 
@@ -254,17 +306,30 @@ 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) ));
-               goto done;
+
+               status = map_nt_error_from_unix(errno);
        }
 
+       notify_fname(conn, NOTIFY_ACTION_REMOVED,
+                    FILE_NOTIFY_CHANGE_FILE_NAME,
+                    fsp->fsp_name);
+
+       /* As we now have POSIX opens which can unlink
+        * with other open files we may have taken
+        * this code path with more than one share mode
+        * entry - ensure we only delete once by resetting
+        * the delete on close flag. JRA.
+        */
+
+       set_delete_on_close_lck(lck, False, NULL);
+
  done:
+
        /* unbecome user. */
        pop_sec_ctx();
        
-       process_pending_change_notify_queue((time_t)0);
-
        TALLOC_FREE(lck);
-       return 0;
+       return status;
 }
 
 /****************************************************************************
@@ -275,12 +340,13 @@ static int close_remove_share_mode(files_struct *fsp, enum file_close_type close
  delete on close is done on normal and shutdown close.
 ****************************************************************************/
 
-static int close_normal_file(files_struct *fsp, enum file_close_type close_type)
+static NTSTATUS close_normal_file(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;
        connection_struct *conn = fsp->conn;
-       int saved_errno = 0;
-       int err = 0;
-       int err1 = 0;
 
        if (fsp->aio_write_behind) {
                /*
@@ -289,8 +355,7 @@ static int close_normal_file(files_struct *fsp, enum file_close_type close_type)
                 */
                int ret = wait_for_aio_completion(fsp);
                if (ret) {
-                       saved_errno = ret;
-                       err1 = -1;
+                       saved_status1 = map_nt_error_from_unix(ret);
                }
        } else {
                cancel_aio_by_fsp(fsp);
@@ -301,15 +366,12 @@ static int close_normal_file(files_struct *fsp, enum file_close_type close_type)
         * error here, we must remember this.
         */
 
-       if (close_filestruct(fsp) == -1) {
-               saved_errno = errno;
-               err1 = -1;
-       }
+       saved_status2 = close_filestruct(fsp);
 
        if (fsp->print_file) {
                print_fsp_end(fsp, close_type);
                file_free(fsp);
-               return 0;
+               return NT_STATUS_OK;
        }
 
        /* If this is an old DOS or FCB open and we have multiple opens on
@@ -318,22 +380,16 @@ static int close_normal_file(files_struct *fsp, enum file_close_type close_type)
 
        if (fsp->fh->ref_count == 1) {
                /* Should we return on error here... ? */
-               close_remove_share_mode(fsp, close_type);
+               saved_status3 = close_remove_share_mode(fsp, close_type);
        }
 
        if(fsp->oplock_type) {
                release_file_oplock(fsp);
        }
 
-       locking_close_file(fsp);
-
-       err = fd_close(conn, fsp);
+       locking_close_file(smbd_messaging_context(), fsp);
 
-       /* Only save errno if fd_close failed and we don't already
-          have an errno saved from a flush call. */
-       if ((err1 != -1) && (err == -1)) {
-               saved_errno = errno;
-       }
+       status = fd_close(conn, fsp);
 
        /* check for magic scripts */
        if (close_type == NORMAL_CLOSE) {
@@ -344,58 +400,96 @@ static int close_normal_file(files_struct *fsp, enum file_close_type close_type)
         * Ensure pending modtime is set after close.
         */
 
-       if(fsp->pending_modtime && fsp->pending_modtime_owner) {
+       if (fsp->pending_modtime_owner && !null_timespec(fsp->pending_modtime)) {
                set_filetime(conn, fsp->fsp_name, fsp->pending_modtime);
-       } else if (fsp->last_write_time) {
+       } else if (!null_timespec(fsp->last_write_time)) {
                set_filetime(conn, fsp->fsp_name, fsp->last_write_time);
        }
 
+       if (NT_STATUS_IS_OK(status)) {
+               if (!NT_STATUS_IS_OK(saved_status1)) {
+                       status = saved_status1;
+               } else if (!NT_STATUS_IS_OK(saved_status2)) {
+                       status = saved_status2;
+               } else if (!NT_STATUS_IS_OK(saved_status3)) {
+                       status = saved_status3;
+               }
+       }
+
        DEBUG(2,("%s closed file %s (numopen=%d) %s\n",
                conn->user,fsp->fsp_name,
                conn->num_files_open,
-               (err == -1 || err1 == -1) ? strerror(saved_errno) : ""));
+               nt_errstr(status) ));
 
        file_free(fsp);
-
-       if (err == -1 || err1 == -1) {
-               errno = saved_errno;
-               return saved_errno;
-       } else {
-               return 0;
-       }
+       return status;
 }
 
 /****************************************************************************
  Close a directory opened by an NT SMB call. 
 ****************************************************************************/
   
-static int close_directory(files_struct *fsp, enum file_close_type close_type)
+static NTSTATUS close_directory(files_struct *fsp, enum file_close_type close_type)
 {
        struct share_mode_lock *lck = 0;
        BOOL delete_dir = False;
+       NTSTATUS status = NT_STATUS_OK;
 
        /*
         * NT can set delete_on_close of the last open
         * reference to a directory also.
         */
 
-       lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, NULL, NULL);
+       lck = get_share_mode_lock(NULL, fsp->file_id, NULL, NULL);
 
        if (lck == NULL) {
                DEBUG(0, ("close_directory: Could not get share mode lock for %s\n", fsp->fsp_name));
-               return EINVAL;
+               return NT_STATUS_INVALID_PARAMETER;
        }
 
        if (!del_share_mode(lck, fsp)) {
                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. If all opens are POSIX delete now. */
+               for (i=0; i<lck->num_share_modes; i++) {
+                       struct share_mode_entry *e = &lck->share_modes[i];
+                       if (is_valid_share_mode_entry(e)) {
+                               if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) {
+                                       continue;
+                               }
+                               delete_dir = False;
+                               break;
+                       }
+               }
+       }
 
        if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) &&
                                delete_dir &&
                                lck->delete_token) {
-               BOOL ok;
        
                /* Become the user who requested the delete. */
 
@@ -411,10 +505,12 @@ static int close_directory(files_struct *fsp, enum file_close_type close_type)
 
                TALLOC_FREE(lck);
 
-               ok = rmdir_internals(fsp->conn, fsp->fsp_name);
+               status = rmdir_internals(talloc_tos(),
+                               fsp->conn, fsp->fsp_name);
 
-               DEBUG(5,("close_directory: %s. Delete on close was set - deleting directory %s.\n",
-                       fsp->fsp_name, ok ? "succeeded" : "failed" ));
+               DEBUG(5,("close_directory: %s. Delete on close was set - "
+                        "deleting directory returned %s.\n",
+                        fsp->fsp_name, nt_errstr(status)));
 
                /* unbecome user. */
                pop_sec_ctx();
@@ -424,12 +520,9 @@ static int close_directory(files_struct *fsp, enum file_close_type close_type)
                 * now fail as the directory has been deleted.
                 */
 
-               if(ok) {
+               if(NT_STATUS_IS_OK(status)) {
                        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(
@@ -441,35 +534,35 @@ static int close_directory(files_struct *fsp, enum file_close_type close_type)
         */
        close_filestruct(fsp);
        file_free(fsp);
-       return 0;
+       return status;
 }
 
 /****************************************************************************
  Close a 'stat file' opened internally.
 ****************************************************************************/
   
-static int close_stat(files_struct *fsp)
+NTSTATUS close_stat(files_struct *fsp)
 {
        /*
         * Do the code common to files and directories.
         */
        close_filestruct(fsp);
        file_free(fsp);
-       return 0;
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
  Close a files_struct.
 ****************************************************************************/
   
-int close_file(files_struct *fsp, enum file_close_type close_type)
+NTSTATUS close_file(files_struct *fsp, enum file_close_type close_type)
 {
-       if(fsp->is_directory)
+       if(fsp->is_directory) {
                return close_directory(fsp, close_type);
-       else if (fsp->is_stat)
+       } else if (fsp->is_stat) {
                return close_stat(fsp);
-       else if (fsp->fake_file_handle != NULL)
+       } else if (fsp->fake_file_handle != NULL) {
                return close_fake_file(fsp);
-       else
-               return close_normal_file(fsp, close_type);
+       }
+       return close_normal_file(fsp, close_type);
 }