Make Samba3 pass the RAW-LOCK test as Windows.
[samba.git] / source3 / smbd / reply.c
index 0b77d82edd94232efd990b1c3e2ba8e545272138..185f6014d1a38d16173e86839250a547c03e26dc 100644 (file)
@@ -2454,24 +2454,6 @@ static NTSTATUS do_unlink(connection_struct *conn,
                return NT_STATUS_OBJECT_NAME_INVALID;
 #endif /* JRATEST */
 
-       /* Fix for bug #3035 from SATOH Fumiyasu <fumiyas@miraclelinux.com>
-
-         On a Windows share, a file with read-only dosmode can be opened with
-         DELETE_ACCESS. But on a Samba share (delete readonly = no), it
-         fails with NT_STATUS_CANNOT_DELETE error.
-
-         This semantic causes a problem that a user can not
-         rename a file with read-only dosmode on a Samba share
-         from a Windows command prompt (i.e. cmd.exe, but can rename
-         from Windows Explorer).
-       */
-
-       if (!lp_delete_readonly(SNUM(conn))) {
-               if (fattr & aRONLY) {
-                       return NT_STATUS_CANNOT_DELETE;
-               }
-       }
-
        /* On open checks the open itself will check the share mode, so
           don't do it here as we'll get it wrong. */
 
@@ -2500,6 +2482,16 @@ static NTSTATUS do_unlink(connection_struct *conn,
                return status;
        }
 
+       status = can_set_delete_on_close(fsp, fattr);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("do_unlink can_set_delete_on_close for file %s - "
+                       "(%s)\n",
+                       smb_fname_str_dbg(smb_fname),
+                       nt_errstr(status)));
+               close_file(req, fsp, NORMAL_CLOSE);
+               return status;
+       }
+
        /* The set is across all open files on this dev/inode pair. */
        if (!set_delete_on_close(fsp, True, &conn->server_info->utok)) {
                close_file(req, fsp, NORMAL_CLOSE);
@@ -2638,7 +2630,7 @@ NTSTATUS unlink_internals(connection_struct *conn, struct smb_request *req,
                                continue;
                        }
 
-                       if(!mask_match(dname, fname_mask, get_Protocol(),
+                       if(!mask_match(dname, fname_mask,
                                       conn->case_sensitive)) {
                                TALLOC_FREE(frame);
                                TALLOC_FREE(talloced);
@@ -2925,8 +2917,7 @@ static void send_file_readbraw(connection_struct *conn,
 
        if ( !req_is_in_chain(req) && (nread > 0) && (fsp->base_fsp == NULL) &&
            (fsp->wcp == NULL) &&
-            lp_use_sendfile(SNUM(conn), get_Protocol(),
-                            smbd_server_conn->smb1.signing_state) ) {
+           lp_use_sendfile(SNUM(conn), smbd_server_conn->smb1.signing_state) ) {
                ssize_t sendfile_read = -1;
                char header[4];
                DATA_BLOB header_blob;
@@ -3453,8 +3444,7 @@ static void send_file_readX(connection_struct *conn, struct smb_request *req,
        if (!req_is_in_chain(req) &&
            !is_encrypted_packet(req->inbuf) && (fsp->base_fsp == NULL) &&
            (fsp->wcp == NULL) &&
-           lp_use_sendfile(SNUM(conn), get_Protocol(),
-                           smbd_server_conn->smb1.signing_state) ) {
+           lp_use_sendfile(SNUM(conn), smbd_server_conn->smb1.signing_state) ) {
                uint8 headerbuf[smb_size + 12 * 2];
                DATA_BLOB header;
 
@@ -5292,265 +5282,6 @@ void reply_mkdir(struct smb_request *req)
        return;
 }
 
-/****************************************************************************
- 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.
-****************************************************************************/
-
-NTSTATUS rmdir_internals(TALLOC_CTX *ctx,
-                        connection_struct *conn,
-                        struct smb_filename *smb_dname)
-{
-       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_in_path(dname, conn->veto_list, get_Protocol(),
-                                      conn->case_sensitive)) {
-                               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;
-}
-
 /****************************************************************************
  Reply to a rmdir.
 ****************************************************************************/
@@ -5562,6 +5293,8 @@ void reply_rmdir(struct smb_request *req)
        char *directory = NULL;
        NTSTATUS status;
        TALLOC_CTX *ctx = talloc_tos();
+       files_struct *fsp = NULL;
+       int info = 0;
        struct smbd_server_connection *sconn = smbd_server_conn;
 
        START_PROFILE(SMBrmdir);
@@ -5594,15 +5327,51 @@ void reply_rmdir(struct smb_request *req)
                goto out;
        }
 
-       dptr_closepath(sconn, smb_dname->base_name, req->smbpid);
-       status = rmdir_internals(ctx, conn, smb_dname);
+       status = SMB_VFS_CREATE_FILE(
+               conn,                                   /* conn */
+               req,                                    /* req */
+               0,                                      /* root_dir_fid */
+               smb_dname,                              /* fname */
+               DELETE_ACCESS,                          /* access_mask */
+               (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
+                       FILE_SHARE_DELETE),
+               FILE_OPEN,                              /* create_disposition*/
+               FILE_DIRECTORY_FILE,                    /* create_options */
+               FILE_ATTRIBUTE_DIRECTORY,               /* file_attributes */
+               0,                                      /* oplock_request */
+               0,                                      /* allocation_size */
+               NULL,                                   /* sd */
+               NULL,                                   /* ea_list */
+               &fsp,                                   /* result */
+               &info);                                 /* pinfo */
+
+       if (!NT_STATUS_IS_OK(status)) {
+               if (open_was_deferred(req->mid)) {
+                       /* We have re-scheduled this call. */
+                       goto out;
+               }
+               reply_nterror(req, status);
+               goto out;
+       }
+
+       status = can_set_delete_on_close(fsp, FILE_ATTRIBUTE_DIRECTORY);
        if (!NT_STATUS_IS_OK(status)) {
+               close_file(req, fsp, ERROR_CLOSE);
                reply_nterror(req, status);
                goto out;
        }
 
+       if (!set_delete_on_close(fsp, true, &conn->server_info->utok)) {
+               close_file(req, fsp, ERROR_CLOSE);
+               reply_nterror(req, NT_STATUS_ACCESS_DENIED);
+               goto out;
+       }
+
+       close_file(req, fsp, NORMAL_CLOSE);
        reply_outbuf(req, 0, 0);
 
+       dptr_closepath(sconn, smb_dname->base_name, req->smbpid);
+
        DEBUG(3, ("rmdir %s\n", smb_fname_str_dbg(smb_dname)));
  out:
        TALLOC_FREE(smb_dname);
@@ -6087,7 +5856,7 @@ NTSTATUS rename_internals_fsp(connection_struct *conn,
                 */
 
                if (create_options & FILE_DELETE_ON_CLOSE) {
-                       status = can_set_delete_on_close(fsp, True, 0);
+                       status = can_set_delete_on_close(fsp, 0);
 
                        if (NT_STATUS_IS_OK(status)) {
                                /* Note that here we set the *inital* delete on close flag,
@@ -6340,8 +6109,7 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx,
                        continue;
                }
 
-               if(!mask_match(dname, fname_src_mask, get_Protocol(),
-                              conn->case_sensitive)) {
+               if(!mask_match(dname, fname_src_mask, conn->case_sensitive)) {
                        TALLOC_FREE(talloced);
                        continue;
                }
@@ -6972,7 +6740,7 @@ void reply_copy(struct smb_request *req)
                                continue;
                        }
 
-                       if(!mask_match(dname, fname_src_mask, get_Protocol(),
+                       if(!mask_match(dname, fname_src_mask,
                                       conn->case_sensitive)) {
                                TALLOC_FREE(talloced);
                                continue;
@@ -7270,6 +7038,30 @@ NTSTATUS smbd_do_locking(struct smb_request *req,
                if (type & LOCKING_ANDX_CANCEL_LOCK) {
                        struct blocking_lock_record *blr = NULL;
 
+                       if (num_locks > 1) {
+                               /*
+                                * MS-CIFS (2.2.4.32.1) states that a cancel is honored if and only
+                                * if the lock vector contains one entry. When given mutliple cancel
+                                * requests in a single PDU we expect the server to return an
+                                * error. Windows servers seem to accept the request but only
+                                * cancel the first lock.
+                                * JRA - Do what Windows does (tm) :-).
+                                */
+
+#if 0
+                               /* MS-CIFS (2.2.4.32.1) behavior. */
+                               return NT_STATUS_DOS(ERRDOS,
+                                               ERRcancelviolation);
+#else
+                               /* Windows behavior. */
+                               if (i != 0) {
+                                       DEBUG(10,("smbd_do_locking: ignoring subsequent "
+                                               "cancel request\n"));
+                                       continue;
+                               }
+#endif
+                       }
+
                        if (lp_blocking_locks(SNUM(conn))) {
 
                                /* Schedule a message to ourselves to