[CIFS] Fix umount --force to wake up the pending response queue, not just
the request queue. Also periodically wakeup response_q so threads can
check if stuck requests have timed out. Workaround Windows server illegal smb
length on transact2 findfirst response.

Signed-off-by: Steve French <sfrench@us.ibm.com>
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 51548ed..f4974b4 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -32,6 +32,7 @@
 #include <linux/seq_file.h>
 #include <linux/vfs.h>
 #include <linux/mempool.h>
+#include <linux/delay.h>
 #include "cifsfs.h"
 #include "cifspdu.h"
 #define DECLARE_GLOBALS_HERE
@@ -429,6 +430,11 @@
 	{
 		cFYI(1,("wake up tasks now - umount begin not complete"));
 		wake_up_all(&tcon->ses->server->request_q);
+		wake_up_all(&tcon->ses->server->response_q);
+		msleep(1); /* yield */
+		/* we have to kick the requests once more */
+		wake_up_all(&tcon->ses->server->response_q);
+		msleep(1);
 	}
 /* BB FIXME - finish add checks for tidStatus BB */
 
@@ -895,6 +901,9 @@
 
 static int cifs_dnotify_thread(void * dummyarg)
 {
+	struct list_head *tmp;
+	struct cifsSesInfo *ses;
+
 	daemonize("cifsdnotifyd");
 	allow_signal(SIGTERM);
 
@@ -903,7 +912,19 @@
 		if(try_to_freeze())
 			continue;
 		set_current_state(TASK_INTERRUPTIBLE);
-		schedule_timeout(39*HZ);
+		schedule_timeout(15*HZ);
+		read_lock(&GlobalSMBSeslock);
+		/* check if any stuck requests that need
+		   to be woken up and wakeq so the
+		   thread can wake up and error out */
+		list_for_each(tmp, &GlobalSMBSessionList) {
+			ses = list_entry(tmp, struct cifsSesInfo, 
+				cifsSessionList);
+			if(ses && ses->server && 
+			     atomic_read(&ses->server->inSend))
+				wake_up_all(&ses->server->response_q);
+		}
+		read_unlock(&GlobalSMBSeslock);
 	} while(!signal_pending(current));
 	complete_and_exit (&cifs_dnotify_exited, 0);
 }
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index d179b0c..6867e55 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -90,6 +90,18 @@
 	   check for tcp and smb session status done differently
 	   for those three - in the calling routine */
 	if(tcon) {
+		if(tcon->tidStatus == CifsExiting) {
+			/* only tree disconnect, open, and write,
+			(and ulogoff which does not have tcon)
+			are allowed as we start force umount */
+			if((smb_command != SMB_COM_WRITE_ANDX) && 
+			   (smb_command != SMB_COM_OPEN_ANDX) && 
+			   (smb_command != SMB_COM_TREE_DISCONNECT)) {
+				cFYI(1,("can not send cmd %d while umounting",
+					smb_command));
+				return -ENODEV;
+			}
+		}
 		if((tcon->ses) && (tcon->ses->status != CifsExiting) &&
 				  (tcon->ses->server)){
 			struct nls_table *nls_codepage;
@@ -187,6 +199,19 @@
 	   check for tcp and smb session status done differently
 	   for those three - in the calling routine */
 	if(tcon) {
+		if(tcon->tidStatus == CifsExiting) {
+			/* only tree disconnect, open, and write,
+			  (and ulogoff which does not have tcon)
+			  are allowed as we start force umount */
+			if((smb_command != SMB_COM_WRITE_ANDX) &&
+			   (smb_command != SMB_COM_OPEN_ANDX) &&
+			   (smb_command != SMB_COM_TREE_DISCONNECT)) {
+				cFYI(1,("can not send cmd %d while umounting",
+					smb_command));
+				return -ENODEV;
+			}
+		}
+
 		if((tcon->ses) && (tcon->ses->status != CifsExiting) && 
 				  (tcon->ses->server)){
 			struct nls_table *nls_codepage;
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index ca27a82..94baf6c 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -397,12 +397,12 @@
 			if(smb->Command == SMB_COM_LOCKING_ANDX)
 				return 0;
 			else
-				cERROR(1, ("Rcvd Request not response "));         
+				cERROR(1, ("Rcvd Request not response"));         
 		}
 	} else { /* bad signature or mid */
 		if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff))
 			cERROR(1,
-			       ("Bad protocol string signature header %x ",
+			       ("Bad protocol string signature header %x",
 				*(unsigned int *) smb->Protocol));
 		if (mid != smb->Mid)
 			cERROR(1, ("Mids do not match"));
@@ -417,7 +417,7 @@
 	__u32 len = smb->smb_buf_length;
 	__u32 clc_len;  /* calculated length */
 	cFYI(0,
-	     ("Entering checkSMB with Length: %x, smb_buf_length: %x ",
+	     ("Entering checkSMB with Length: %x, smb_buf_length: %x",
 	      length, len));
 	if (((unsigned int)length < 2 + sizeof (struct smb_hdr)) ||
 	    (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4)) {
@@ -451,9 +451,16 @@
 		cERROR(1, ("bad smb size detected for Mid=%d", smb->Mid));
 		/* Windows XP can return a few bytes too much, presumably
 		an illegal pad, at the end of byte range lock responses 
-		so we allow for up to eight byte pad, as long as actual
+		so we allow for that three byte pad, as long as actual
 		received length is as long or longer than calculated length */
-		if((4+len > clc_len) && (len <= clc_len + 3))
+		/* We have now had to extend this more, since there is a 
+		case in which it needs to be bigger still to handle a
+		malformed response to transact2 findfirst from WinXP when
+		access denied is returned and thus bcc and wct are zero
+		but server says length is 0x21 bytes too long as if the server
+		forget to reset the smb rfc1001 length when it reset the
+		wct and bcc to minimum size and drop the t2 parms and data */
+		if((4+len > clc_len) && (len <= clc_len + 512))
 			return 0;
 		else
 			return 1;
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
index f781468..5de74d2 100644
--- a/fs/cifs/netmisc.c
+++ b/fs/cifs/netmisc.c
@@ -330,7 +330,7 @@
 	ERRHRD, ERRgeneral, NT_STATUS_ACCOUNT_RESTRICTION}, {
 	ERRSRV, 2241, NT_STATUS_INVALID_LOGON_HOURS}, {
 	ERRSRV, 2240, NT_STATUS_INVALID_WORKSTATION}, {
-	ERRSRV, 2242, NT_STATUS_PASSWORD_EXPIRED}, {
+	ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_EXPIRED}, {
 	ERRSRV, 2239, NT_STATUS_ACCOUNT_DISABLED}, {
 	ERRHRD, ERRgeneral, NT_STATUS_NONE_MAPPED}, {
 	ERRHRD, ERRgeneral, NT_STATUS_TOO_MANY_LUIDS_REQUESTED}, {
@@ -676,7 +676,7 @@
 	ERRDOS, 193, NT_STATUS_IMAGE_CHECKSUM_MISMATCH}, {
 	ERRHRD, ERRgeneral, NT_STATUS_LOST_WRITEBEHIND_DATA}, {
 	ERRHRD, ERRgeneral, NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID}, {
-	ERRSRV, 2242, NT_STATUS_PASSWORD_MUST_CHANGE}, {
+	ERRSRV, ERRpasswordExpired, NT_STATUS_PASSWORD_MUST_CHANGE}, {
 	ERRHRD, ERRgeneral, NT_STATUS_NOT_FOUND}, {
 	ERRHRD, ERRgeneral, NT_STATUS_NOT_TINY_STREAM}, {
 	ERRHRD, ERRgeneral, NT_STATUS_RECOVERY_FAILURE}, {
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 41a9659..f887119 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -515,6 +515,7 @@
 			*pbytes_returned = in_buf->smb_buf_length;
 
 			/* BB special case reconnect tid and uid here? */
+			/* BB special case Errbadpassword and pwdexpired here */
 			rc = map_smb_to_linux_error(in_buf);
 
 			/* convert ByteCount if necessary */