try to fix SMB_ASSERT(got_token)... TODO
[metze/samba/wip.git] / source3 / smbd / close.c
index 816c5d98bd6d5a77c33af448ddb99f6caef5e07c..bdd177cca8f38eb43347cf9ed48eb437f85138da 100644 (file)
 */
 
 #include "includes.h"
-
-extern struct current_user current_user;
+#include "system/filesys.h"
+#include "printing.h"
+#include "smbd/smbd.h"
+#include "smbd/globals.h"
+#include "fake_file.h"
+#include "transfer_file.h"
+#include "auth.h"
+#include "messages.h"
+#include "../librpc/gen_ndr/open_files.h"
 
 /****************************************************************************
  Run a file if it is a magic script.
@@ -103,7 +110,7 @@ static NTSTATUS check_magic(struct files_struct *fsp)
                goto out;
        }
 
-       if (sys_fstat(tmp_fd, &st, lp_fake_dir_create_times()) == -1) {
+       if (sys_fstat(tmp_fd, &st, false) == -1) {
                int err = errno;
                close(tmp_fd);
                close(outfd);
@@ -111,7 +118,7 @@ static NTSTATUS check_magic(struct files_struct *fsp)
                goto out;
        }
 
-       if (transfer_file(tmp_fd,outfd,(SMB_OFF_T)st.st_ex_size) == (SMB_OFF_T)-1) {
+       if (transfer_file(tmp_fd,outfd,(off_t)st.st_ex_size) == (off_t)-1) {
                int err = errno;
                close(tmp_fd);
                close(outfd);
@@ -149,25 +156,81 @@ static NTSTATUS close_filestruct(files_struct *fsp)
        return status;
 }
 
+static int compare_share_mode_times(const void *p1, const void *p2)
+{
+       const struct share_mode_entry *s1 = (const struct share_mode_entry *)p1;
+       const struct share_mode_entry *s2 = (const struct share_mode_entry *)p2;
+       return timeval_compare(&s1->time, &s2->time);
+}
+
 /****************************************************************************
  If any deferred opens are waiting on this close, notify them.
 ****************************************************************************/
 
-static void notify_deferred_opens(struct share_mode_lock *lck)
+static void notify_deferred_opens(struct smbd_server_connection *sconn,
+                                 struct share_mode_lock *lck)
 {
-       int i;
+       uint32_t i, num_deferred;
+       struct share_mode_entry *deferred;
 
-       if (!should_notify_deferred_opens()) {
+       if (!should_notify_deferred_opens(sconn)) {
                return;
        }
-       for (i=0; i<lck->num_share_modes; i++) {
-               struct share_mode_entry *e = &lck->share_modes[i];
-               if (!is_deferred_open_entry(e)) {
-                       continue;
-               }
+
+       num_deferred = 0;
+       for (i=0; i<lck->data->num_share_modes; i++) {
+               struct share_mode_entry *e = &lck->data->share_modes[i];
+
+               if (!is_deferred_open_entry(e)) {
+                       continue;
+               }
+               if (share_mode_stale_pid(lck->data, i)) {
+                       continue;
+               }
+               num_deferred += 1;
+       }
+       if (num_deferred == 0) {
+               return;
+       }
+
+       deferred = talloc_array(talloc_tos(), struct share_mode_entry,
+                               num_deferred);
+       if (deferred == NULL) {
+               return;
+       }
+
+       num_deferred = 0;
+       for (i=0; i<lck->data->num_share_modes; i++) {
+               struct share_mode_entry *e = &lck->data->share_modes[i];
+               if (is_deferred_open_entry(e)) {
+                       deferred[num_deferred] = *e;
+                       num_deferred += 1;
+               }
+       }
+
+       /*
+        * We need to sort the notifications by initial request time. Imagine
+        * two opens come in asyncronously, both conflicting with the open we
+        * just close here. If we don't sort the notifications, the one that
+        * came in last might get the response before the one that came in
+        * first. This is demonstrated with the smbtorture4 raw.mux test.
+        *
+        * As long as we had the UNUSED_SHARE_MODE_ENTRY, we happened to
+        * survive this particular test. Without UNUSED_SHARE_MODE_ENTRY, we
+        * shuffle the share mode entries around a bit, so that we do not
+        * survive raw.mux anymore.
+        *
+        * We could have kept the ordering in del_share_mode, but as the
+        * ordering was never formalized I think it is better to do it here
+        * where it is necessary.
+        */
+
+       qsort(deferred, num_deferred, sizeof(struct share_mode_entry),
+             compare_share_mode_times);
+
+       for (i=0; i<num_deferred; i++) {
+               struct share_mode_entry *e = &deferred[i];
+
                if (procid_is_me(&e->pid)) {
                        /*
                         * We need to notify ourself to retry the open.  Do
@@ -175,18 +238,19 @@ static void notify_deferred_opens(struct share_mode_lock *lck)
                         * the head of the queue and changing the wait time to
                         * zero.
                         */
-                       schedule_deferred_open_smb_message(e->op_mid);
+                       schedule_deferred_open_message_smb(sconn, e->op_mid);
                } else {
                        char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
 
                        share_mode_entry_to_message(msg, e);
 
-                       messaging_send_buf(smbd_messaging_context(),
-                                          e->pid, MSG_SMB_OPEN_RETRY,
+                       messaging_send_buf(sconn->msg_ctx, e->pid,
+                                          MSG_SMB_OPEN_RETRY,
                                           (uint8 *)msg,
                                           MSG_SMB_SHARE_MODE_ENTRY_SIZE);
                }
        }
+       TALLOC_FREE(deferred);
 }
 
 /****************************************************************************
@@ -195,14 +259,14 @@ static void notify_deferred_opens(struct share_mode_lock *lck)
 
 NTSTATUS delete_all_streams(connection_struct *conn, const char *fname)
 {
-       struct stream_struct *stream_info;
+       struct stream_struct *stream_info = NULL;
        int i;
-       unsigned int num_streams;
+       unsigned int num_streams = 0;
        TALLOC_CTX *frame = talloc_stackframe();
        NTSTATUS status;
 
-       status = SMB_VFS_STREAMINFO(conn, NULL, fname, talloc_tos(),
-                                   &num_streams, &stream_info);
+       status = vfs_streaminfo(conn, NULL, fname, talloc_tos(),
+                               &num_streams, &stream_info);
 
        if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
                DEBUG(10, ("no streams around\n"));
@@ -211,7 +275,7 @@ NTSTATUS delete_all_streams(connection_struct *conn, const char *fname)
        }
 
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(10, ("SMB_VFS_STREAMINFO failed: %s\n",
+               DEBUG(10, ("vfs_streaminfo failed: %s\n",
                           nt_errstr(status)));
                goto fail;
        }
@@ -273,10 +337,13 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
        NTSTATUS status = NT_STATUS_OK;
        NTSTATUS tmp_status;
        struct file_id id;
+       const struct security_unix_token *del_token = NULL;
+       const struct security_token *del_nt_token = NULL;
+       bool got_tokens = false;
 
        /* Ensure any pending write time updates are done. */
        if (fsp->update_write_time_event) {
-               update_write_time_handler(smbd_event_context(),
+               update_write_time_handler(fsp->conn->sconn->ev_ctx,
                                        fsp->update_write_time_event,
                                        timeval_current(),
                                        (void *)fsp);
@@ -288,9 +355,7 @@ 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,
-                                 NULL);
-
+       lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
        if (lck == NULL) {
                DEBUG(0, ("close_remove_share_mode: Could not get share mode "
                          "lock for file %s\n", fsp_str_dbg(fsp)));
@@ -302,7 +367,7 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
                DEBUG(10,("close_remove_share_mode: write time forced "
                        "for file %s\n",
                        fsp_str_dbg(fsp)));
-               set_close_write_time(fsp, lck->changed_write_time);
+               set_close_write_time(fsp, lck->data->changed_write_time);
        } else if (fsp->update_write_time_on_close) {
                /* Someone had a pending write. */
                if (null_timespec(fsp->close_write_time)) {
@@ -320,40 +385,46 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
                }
        }
 
-       if (!del_share_mode(lck, fsp)) {
-               DEBUG(0, ("close_remove_share_mode: Could not delete share "
-                         "entry for file %s\n",
-                         fsp_str_dbg(fsp)));
-       }
-
-       if (fsp->initial_delete_on_close && (lck->delete_token == NULL)) {
+       if (fsp->initial_delete_on_close &&
+                       !is_delete_on_close_set(lck, fsp->name_hash)) {
                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) {
+               if (get_current_vuid(conn) != fsp->vuid) {
                        become_user(conn, fsp->vuid);
                        became_user = True;
                }
-               set_delete_on_close_lck(lck, True, &current_user.ut);
+               fsp->delete_on_close = true;
+               set_delete_on_close_lck(fsp, lck, True,
+                               get_current_nttok(conn),
+                               get_current_utok(conn));
                if (became_user) {
                        unbecome_user();
                }
        }
 
-       delete_file = lck->delete_on_close;
+       delete_file = is_delete_on_close_set(lck, fsp->name_hash);
 
        if (delete_file) {
                int i;
-               /* See if others still have the file 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)) {
+               /* See if others still have the file open via this pathname.
+                  If this is the case, then don't delete. If all opens are
+                  POSIX delete now. */
+               for (i=0; i<lck->data->num_share_modes; i++) {
+                       struct share_mode_entry *e = &lck->data->share_modes[i];
+
+                       //TODO: continue if our own entry...
+
+                       if (is_valid_share_mode_entry(e) &&
+                                       e->name_hash == fsp->name_hash) {
                                if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) {
                                        continue;
                                }
+                               if (share_mode_stale_pid(lck->data, i)) {
+                                       continue;
+                               }
                                delete_file = False;
                                break;
                        }
@@ -361,7 +432,7 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
        }
 
        /* Notify any deferred opens waiting on this close. */
-       notify_deferred_opens(lck);
+       notify_deferred_opens(conn->sconn, lck);
        reply_to_oplock_break_requests(fsp);
 
        /*
@@ -369,9 +440,14 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
         * reference to a file.
         */
 
-       if (!(close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE)
-           || !delete_file
-           || (lck->delete_token == NULL)) {
+       if (!(close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) ||
+                       !delete_file) {
+               if (!del_share_mode(lck, fsp)) {
+                       DEBUG(0, ("close_remove_share_mode: Could not delete share "
+                                 "entry for file %s\n",
+                                 fsp_str_dbg(fsp)));
+               }
+
                TALLOC_FREE(lck);
                return NT_STATUS_OK;
        }
@@ -388,24 +464,28 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
         */
        fsp->update_write_time_on_close = false;
 
-       if (!unix_token_equal(lck->delete_token, &current_user.ut)) {
+       got_tokens = get_delete_on_close_token(lck, fsp->name_hash,
+                                       &del_nt_token, &del_token);
+       SMB_ASSERT(got_tokens);
+
+       if (!unix_token_equal(del_token, get_current_utok(conn))) {
                /* Become the user who requested the delete. */
 
                DEBUG(5,("close_remove_share_mode: file %s. "
                        "Change user to uid %u\n",
                        fsp_str_dbg(fsp),
-                       (unsigned int)lck->delete_token->uid));
+                       (unsigned int)del_token->uid));
 
                if (!push_sec_ctx()) {
                        smb_panic("close_remove_share_mode: file %s. failed to push "
                                  "sec_ctx.\n");
                }
 
-               set_sec_ctx(lck->delete_token->uid,
-                           lck->delete_token->gid,
-                           lck->delete_token->ngroups,
-                           lck->delete_token->groups,
-                           NULL);
+               set_sec_ctx(del_token->uid,
+                           del_token->gid,
+                           del_token->ngroups,
+                           del_token->groups,
+                           del_nt_token);
 
                changed_user = true;
        }
@@ -470,10 +550,6 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
                status = map_nt_error_from_unix(errno);
        }
 
-       notify_fname(conn, NOTIFY_ACTION_REMOVED,
-                    FILE_NOTIFY_CHANGE_FILE_NAME,
-                    fsp->fsp_name->base_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
@@ -481,7 +557,8 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
         * the delete on close flag. JRA.
         */
 
-       set_delete_on_close_lck(lck, False, NULL);
+       fsp->delete_on_close = false;
+       set_delete_on_close_lck(fsp, lck, false, NULL, NULL);
 
  done:
 
@@ -490,7 +567,33 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp,
                pop_sec_ctx();
        }
 
+       if (delete_file) {
+               if (!del_share_mode(lck, fsp)) {
+                       DEBUG(0, ("close_remove_share_mode: Could not delete share "
+                                 "entry for file %s\n",
+                                 fsp_str_dbg(fsp)));
+               }
+       }
+
        TALLOC_FREE(lck);
+
+       if (delete_file) {
+               /*
+                * Do the notification after we released the share
+                * mode lock. Inside notify_fname we take out another
+                * tdb lock. With ctdb also accessing our databases,
+                * this can lead to deadlocks. Putting this notify
+                * after the TALLOC_FREE(lck) above we avoid locking
+                * two records simultaneously. Notifies are async and
+                * informational only, so calling the notify_fname
+                * without holding the share mode lock should not do
+                * any harm.
+                */
+               notify_fname(conn, NOTIFY_ACTION_REMOVED,
+                            FILE_NOTIFY_CHANGE_FILE_NAME,
+                            fsp->fsp_name->base_name);
+       }
+
        return status;
 }
 
@@ -533,23 +636,41 @@ static NTSTATUS update_write_time_on_close(struct files_struct *fsp)
                return NT_STATUS_OK;
        }
 
-       /* On close if we're changing the real file time we
-        * must update it in the open file db too. */
-       (void)set_write_time(fsp->file_id, fsp->close_write_time);
+       /*
+        * get_existing_share_mode_lock() isn't really the right
+        * call here, as we're being called after
+        * close_remove_share_mode() inside close_normal_file()
+        * so it's quite normal to not have an existing share
+        * mode here. However, get_share_mode_lock() doesn't
+        * work because that will create a new share mode if
+        * one doesn't exist - so stick with this call (just
+        * ignore any error we get if the share mode doesn't
+        * exist.
+        */
 
-       lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, NULL);
+       lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
        if (lck) {
+               /* On close if we're changing the real file time we
+                * must update it in the open file db too. */
+               (void)set_write_time(fsp->file_id, fsp->close_write_time);
+
                /* Close write times overwrite sticky write times
                   so we must replace any sticky write time here. */
-               if (!null_timespec(lck->changed_write_time)) {
+               if (!null_timespec(lck->data->changed_write_time)) {
                        (void)set_sticky_write_time(fsp->file_id, fsp->close_write_time);
                }
                TALLOC_FREE(lck);
        }
 
        ft.mtime = fsp->close_write_time;
+       /* As this is a close based update, we are not directly changing the
+          file attributes from a client call, but indirectly from a write. */
        status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name, &ft, false);
        if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10,("update_write_time_on_close: smb_set_file_time "
+                       "on file %s returned %s\n",
+                       fsp_str_dbg(fsp),
+                       nt_errstr(status)));
                return status;
        }
 
@@ -578,21 +699,18 @@ static NTSTATUS close_normal_file(struct smb_request *req, files_struct *fsp,
        NTSTATUS status = NT_STATUS_OK;
        NTSTATUS tmp;
        connection_struct *conn = fsp->conn;
+       int ret;
 
-       if (fsp->aio_write_behind) {
-               /*
-                * If we're finishing write behind on a close we can get a write
-                * error here, we must remember this.
-                */
-               int ret = wait_for_aio_completion(fsp);
-               if (ret) {
-                       status = ntstatus_keeperror(
-                               status, map_nt_error_from_unix(ret));
-               }
-       } else {
-               cancel_aio_by_fsp(fsp);
+       /*
+        * If we're finishing async io on a close we can get a write
+        * error here, we must remember this.
+        */
+       ret = wait_for_aio_completion(fsp);
+       if (ret) {
+               status = ntstatus_keeperror(
+                       status, map_nt_error_from_unix(ret));
        }
+
        /*
         * If we're flushing on a close we can get a write
         * error here, we must remember this.
@@ -602,7 +720,8 @@ static NTSTATUS close_normal_file(struct smb_request *req, files_struct *fsp,
        status = ntstatus_keeperror(status, tmp);
 
        if (fsp->print_file) {
-               print_fsp_end(fsp, close_type);
+               /* FIXME: return spool errors */
+               print_spool_end(fsp, close_type);
                file_free(req, fsp);
                return NT_STATUS_OK;
        }
@@ -622,7 +741,7 @@ static NTSTATUS close_normal_file(struct smb_request *req, files_struct *fsp,
                status = ntstatus_keeperror(status, tmp);
        }
 
-       locking_close_file(smbd_messaging_context(), fsp);
+       locking_close_file(conn->sconn->msg_ctx, fsp, close_type);
 
        tmp = fd_close(fsp);
        status = ntstatus_keeperror(status, tmp);
@@ -649,13 +768,270 @@ static NTSTATUS close_normal_file(struct smb_request *req, files_struct *fsp,
        status = ntstatus_keeperror(status, tmp);
 
        DEBUG(2,("%s closed file %s (numopen=%d) %s\n",
-               conn->server_info->unix_name, fsp_str_dbg(fsp),
+               conn->session_info->unix_info->unix_name, fsp_str_dbg(fsp),
                conn->num_files_open - 1,
                nt_errstr(status) ));
 
        file_free(req, fsp);
        return status;
 }
+/****************************************************************************
+ Static function used by reply_rmdir to delete an entire directory
+ tree recursively. Return True on ok, False on fail.
+****************************************************************************/
+
+static bool recursive_rmdir(TALLOC_CTX *ctx,
+                       connection_struct *conn,
+                       struct smb_filename *smb_dname)
+{
+       const char *dname = NULL;
+       char *talloced = NULL;
+       bool ret = True;
+       long offset = 0;
+       SMB_STRUCT_STAT st;
+       struct smb_Dir *dir_hnd;
+
+       SMB_ASSERT(!is_ntfs_stream_smb_fname(smb_dname));
+
+       dir_hnd = OpenDir(talloc_tos(), conn, smb_dname->base_name, NULL, 0);
+       if(dir_hnd == NULL)
+               return False;
+
+       while((dname = ReadDirName(dir_hnd, &offset, &st, &talloced))) {
+               struct smb_filename *smb_dname_full = NULL;
+               char *fullname = NULL;
+               bool do_break = true;
+               NTSTATUS status;
+
+               if (ISDOT(dname) || ISDOTDOT(dname)) {
+                       TALLOC_FREE(talloced);
+                       continue;
+               }
+
+               if (!is_visible_file(conn, smb_dname->base_name, dname, &st,
+                                    false)) {
+                       TALLOC_FREE(talloced);
+                       continue;
+               }
+
+               /* Construct the full name. */
+               fullname = talloc_asprintf(ctx,
+                               "%s/%s",
+                               smb_dname->base_name,
+                               dname);
+               if (!fullname) {
+                       errno = ENOMEM;
+                       goto err_break;
+               }
+
+               status = create_synthetic_smb_fname(talloc_tos(), fullname,
+                                                   NULL, NULL,
+                                                   &smb_dname_full);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto err_break;
+               }
+
+               if(SMB_VFS_LSTAT(conn, smb_dname_full) != 0) {
+                       goto err_break;
+               }
+
+               if(smb_dname_full->st.st_ex_mode & S_IFDIR) {
+                       if(!recursive_rmdir(ctx, conn, smb_dname_full)) {
+                               goto err_break;
+                       }
+                       if(SMB_VFS_RMDIR(conn,
+                                        smb_dname_full->base_name) != 0) {
+                               goto err_break;
+                       }
+               } else if(SMB_VFS_UNLINK(conn, smb_dname_full) != 0) {
+                       goto err_break;
+               }
+
+               /* Successful iteration. */
+               do_break = false;
+
+        err_break:
+               TALLOC_FREE(smb_dname_full);
+               TALLOC_FREE(fullname);
+               TALLOC_FREE(talloced);
+               if (do_break) {
+                       ret = false;
+                       break;
+               }
+       }
+       TALLOC_FREE(dir_hnd);
+       return ret;
+}
+
+/****************************************************************************
+ The internals of the rmdir code - called elsewhere.
+****************************************************************************/
+
+static NTSTATUS rmdir_internals(TALLOC_CTX *ctx, files_struct *fsp)
+{
+       connection_struct *conn = fsp->conn;
+       struct smb_filename *smb_dname = fsp->fsp_name;
+       int ret;
+
+       SMB_ASSERT(!is_ntfs_stream_smb_fname(smb_dname));
+
+       /* Might be a symlink. */
+       if(SMB_VFS_LSTAT(conn, smb_dname) != 0) {
+               return map_nt_error_from_unix(errno);
+       }
+
+       if (S_ISLNK(smb_dname->st.st_ex_mode)) {
+               /* Is what it points to a directory ? */
+               if(SMB_VFS_STAT(conn, smb_dname) != 0) {
+                       return map_nt_error_from_unix(errno);
+               }
+               if (!(S_ISDIR(smb_dname->st.st_ex_mode))) {
+                       return NT_STATUS_NOT_A_DIRECTORY;
+               }
+               ret = SMB_VFS_UNLINK(conn, smb_dname);
+       } else {
+               ret = SMB_VFS_RMDIR(conn, smb_dname->base_name);
+       }
+       if (ret == 0) {
+               notify_fname(conn, NOTIFY_ACTION_REMOVED,
+                            FILE_NOTIFY_CHANGE_DIR_NAME,
+                            smb_dname->base_name);
+               return NT_STATUS_OK;
+       }
+
+       if(((errno == ENOTEMPTY)||(errno == EEXIST)) && *lp_veto_files(SNUM(conn))) {
+               /*
+                * Check to see if the only thing in this directory are
+                * vetoed files/directories. If so then delete them and
+                * retry. If we fail to delete any of them (and we *don't*
+                * do a recursive delete) then fail the rmdir.
+                */
+               SMB_STRUCT_STAT st;
+               const char *dname = NULL;
+               char *talloced = NULL;
+               long dirpos = 0;
+               struct smb_Dir *dir_hnd = OpenDir(talloc_tos(), conn,
+                                                 smb_dname->base_name, NULL,
+                                                 0);
+
+               if(dir_hnd == NULL) {
+                       errno = ENOTEMPTY;
+                       goto err;
+               }
+
+               while ((dname = ReadDirName(dir_hnd, &dirpos, &st,
+                                           &talloced)) != NULL) {
+                       if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) {
+                               TALLOC_FREE(talloced);
+                               continue;
+                       }
+                       if (!is_visible_file(conn, smb_dname->base_name, dname,
+                                            &st, false)) {
+                               TALLOC_FREE(talloced);
+                               continue;
+                       }
+                       if(!IS_VETO_PATH(conn, dname)) {
+                               TALLOC_FREE(dir_hnd);
+                               TALLOC_FREE(talloced);
+                               errno = ENOTEMPTY;
+                               goto err;
+                       }
+                       TALLOC_FREE(talloced);
+               }
+
+               /* We only have veto files/directories.
+                * Are we allowed to delete them ? */
+
+               if(!lp_recursive_veto_delete(SNUM(conn))) {
+                       TALLOC_FREE(dir_hnd);
+                       errno = ENOTEMPTY;
+                       goto err;
+               }
+
+               /* Do a recursive delete. */
+               RewindDir(dir_hnd,&dirpos);
+               while ((dname = ReadDirName(dir_hnd, &dirpos, &st,
+                                           &talloced)) != NULL) {
+                       struct smb_filename *smb_dname_full = NULL;
+                       char *fullname = NULL;
+                       bool do_break = true;
+                       NTSTATUS status;
+
+                       if (ISDOT(dname) || ISDOTDOT(dname)) {
+                               TALLOC_FREE(talloced);
+                               continue;
+                       }
+                       if (!is_visible_file(conn, smb_dname->base_name, dname,
+                                            &st, false)) {
+                               TALLOC_FREE(talloced);
+                               continue;
+                       }
+
+                       fullname = talloc_asprintf(ctx,
+                                       "%s/%s",
+                                       smb_dname->base_name,
+                                       dname);
+
+                       if(!fullname) {
+                               errno = ENOMEM;
+                               goto err_break;
+                       }
+
+                       status = create_synthetic_smb_fname(talloc_tos(),
+                                                           fullname, NULL,
+                                                           NULL,
+                                                           &smb_dname_full);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               errno = map_errno_from_nt_status(status);
+                               goto err_break;
+                       }
+
+                       if(SMB_VFS_LSTAT(conn, smb_dname_full) != 0) {
+                               goto err_break;
+                       }
+                       if(smb_dname_full->st.st_ex_mode & S_IFDIR) {
+                               if(!recursive_rmdir(ctx, conn,
+                                                   smb_dname_full)) {
+                                       goto err_break;
+                               }
+                               if(SMB_VFS_RMDIR(conn,
+                                       smb_dname_full->base_name) != 0) {
+                                       goto err_break;
+                               }
+                       } else if(SMB_VFS_UNLINK(conn, smb_dname_full) != 0) {
+                               goto err_break;
+                       }
+
+                       /* Successful iteration. */
+                       do_break = false;
+
+                err_break:
+                       TALLOC_FREE(fullname);
+                       TALLOC_FREE(smb_dname_full);
+                       TALLOC_FREE(talloced);
+                       if (do_break)
+                               break;
+               }
+               TALLOC_FREE(dir_hnd);
+               /* Retry the rmdir */
+               ret = SMB_VFS_RMDIR(conn, smb_dname->base_name);
+       }
+
+  err:
+
+       if (ret != 0) {
+               DEBUG(3,("rmdir_internals: couldn't remove directory %s : "
+                        "%s\n", smb_fname_str_dbg(smb_dname),
+                        strerror(errno)));
+               return map_nt_error_from_unix(errno);
+       }
+
+       notify_fname(conn, NOTIFY_ACTION_REMOVED,
+                    FILE_NOTIFY_CHANGE_DIR_NAME,
+                    smb_dname->base_name);
+
+       return NT_STATUS_OK;
+}
 
 /****************************************************************************
  Close a directory opened by an NT SMB call. 
@@ -667,15 +1043,16 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp,
        struct share_mode_lock *lck = NULL;
        bool delete_dir = False;
        NTSTATUS status = NT_STATUS_OK;
+       NTSTATUS status1 = NT_STATUS_OK;
+       const struct security_token *del_nt_token = NULL;
+       const struct security_unix_token *del_token = NULL;
 
        /*
         * NT can set delete_on_close of the last open
         * reference to a directory also.
         */
 
-       lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL,
-                                 NULL);
-
+       lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
        if (lck == NULL) {
                DEBUG(0, ("close_directory: Could not get share mode lock for "
                          "%s\n", fsp_str_dbg(fsp)));
@@ -695,29 +1072,38 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp,
                 * directories we don't care if anyone else
                 * wrote a real delete on close. */
 
-               if (current_user.vuid != fsp->vuid) {
+               if (get_current_vuid(fsp->conn) != fsp->vuid) {
                        become_user(fsp->conn, fsp->vuid);
                        became_user = True;
                }
-               send_stat_cache_delete_message(fsp->fsp_name->base_name);
-               set_delete_on_close_lck(lck, True, &current_user.ut);
+               send_stat_cache_delete_message(fsp->conn->sconn->msg_ctx,
+                                              fsp->fsp_name->base_name);
+               set_delete_on_close_lck(fsp, lck, true,
+                               get_current_nttok(fsp->conn),
+                               get_current_utok(fsp->conn));
+               fsp->delete_on_close = true;
                if (became_user) {
                        unbecome_user();
                }
        }
 
-       delete_dir = lck->delete_on_close;
+       delete_dir = get_delete_on_close_token(lck, fsp->name_hash,
+                                       &del_nt_token, &del_token);
 
        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)) {
+               for (i=0; i<lck->data->num_share_modes; i++) {
+                       struct share_mode_entry *e = &lck->data->share_modes[i];
+                       if (is_valid_share_mode_entry(e) &&
+                                       e->name_hash == fsp->name_hash) {
                                if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) {
                                        continue;
                                }
+                               if (share_mode_stale_pid(lck->data, i)) {
+                                       continue;
+                               }
                                delete_dir = False;
                                break;
                        }
@@ -725,8 +1111,7 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp,
        }
 
        if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) &&
-                               delete_dir &&
-                               lck->delete_token) {
+                               delete_dir) {
        
                /* Become the user who requested the delete. */
 
@@ -734,16 +1119,26 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp,
                        smb_panic("close_directory: failed to push sec_ctx.\n");
                }
 
-               set_sec_ctx(lck->delete_token->uid,
-                               lck->delete_token->gid,
-                               lck->delete_token->ngroups,
-                               lck->delete_token->groups,
-                               NULL);
+               set_sec_ctx(del_token->uid,
+                               del_token->gid,
+                               del_token->ngroups,
+                               del_token->groups,
+                               del_nt_token);
 
                TALLOC_FREE(lck);
 
-               status = rmdir_internals(talloc_tos(), fsp->conn,
-                                        fsp->fsp_name);
+               if ((fsp->conn->fs_capabilities & FILE_NAMED_STREAMS)
+                   && !is_ntfs_stream_smb_fname(fsp->fsp_name)) {
+
+                       status = delete_all_streams(fsp->conn, fsp->fsp_name->base_name);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DEBUG(5, ("delete_all_streams failed: %s\n",
+                                         nt_errstr(status)));
+                               goto out;
+                       }
+               }
+
+               status = rmdir_internals(talloc_tos(), fsp);
 
                DEBUG(5,("close_directory: %s. Delete on close was set - "
                         "deleting directory returned %s.\n",
@@ -766,18 +1161,14 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp,
                        fsp, NT_STATUS_OK);
        }
 
-       status = fd_close(fsp);
+       status1 = fd_close(fsp);
 
-       if (!NT_STATUS_IS_OK(status)) {
+       if (!NT_STATUS_IS_OK(status1)) {
                DEBUG(0, ("Could not close dir! fname=%s, fd=%d, err=%d=%s\n",
                          fsp_str_dbg(fsp), fsp->fh->fd, errno,
                          strerror(errno)));
        }
 
-       if (fsp->dptr) {
-               dptr_CloseDir(fsp->dptr);
-       }
-
        /*
         * Do the code common to files and directories.
         */
@@ -786,6 +1177,9 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp,
 
  out:
        TALLOC_FREE(lck);
+       if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(status1)) {
+               status = status1;
+       }
        return status;
 }
 
@@ -838,6 +1232,9 @@ void msg_close_file(struct messaging_context *msg_ctx,
 {
        files_struct *fsp = NULL;
        struct share_mode_entry e;
+       struct smbd_server_connection *sconn =
+               talloc_get_type_abort(private_data,
+               struct smbd_server_connection);
 
        message_to_share_mode_entry(&e, (char *)data->data);
 
@@ -851,7 +1248,7 @@ void msg_close_file(struct messaging_context *msg_ctx,
                TALLOC_FREE(sm_str);
        }
 
-       fsp = file_find_dif(e.id, e.share_file_id);
+       fsp = file_find_dif(sconn, e.id, e.share_file_id);
        if (!fsp) {
                DEBUG(10,("msg_close_file: failed to find file.\n"));
                return;