Merge with /pub/scm/linux/kernel/git/torvalds/linux-2.6.git
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES
index 6bded10..943ef9b 100644
--- a/fs/cifs/CHANGES
+++ b/fs/cifs/CHANGES
@@ -1,10 +1,12 @@
 Version 1.39
 ------------
-Defer close of a file handle slightly if pending writes depend on that file handle
+Defer close of a file handle slightly if pending writes depend on that handle
 (this reduces the EBADF bad file handle errors that can be logged under heavy
 stress on writes). Modify cifs Kconfig options to expose CONFIG_CIFS_STATS2 
-Fix SFU style symlinks and mknod needed for servers which do not support the CIFS
-Unix Extensions.  Fix setfacl/getfacl on bigendian. 
+Fix SFU style symlinks and mknod needed for servers which do not support the
+CIFS Unix Extensions.  Fix setfacl/getfacl on bigendian. Timeout negative
+dentries so files that the client sees as deleted but that later get created
+on the server will be recognized.  Add client side permission check on setattr.
 
 Version 1.38
 ------------
diff --git a/fs/cifs/README b/fs/cifs/README
index bb909418..e5d09a2 100644
--- a/fs/cifs/README
+++ b/fs/cifs/README
@@ -278,7 +278,9 @@
 		(such as Windows), permissions can also be checked at the
 		client, and a crude form of client side permission checking 
 		can be enabled by specifying file_mode and dir_mode on 
-		the client
+		the client.  Note that the mount.cifs helper must be
+		at version 1.10 or higher to support specifying the uid
+		(or gid) in non-numberic form.
   gid		If CIFS Unix extensions are not supported by the server
 		this overrides the default gid for inodes.
   file_mode     If CIFS Unix extensions are not supported by the server
@@ -345,7 +347,10 @@
 		client system. It is typically only needed when the server
 		supports the CIFS Unix Extensions but the UIDs/GIDs on the
 		client and server system do not match closely enough to allow
-		access by the user doing the mount.
+		access by the user doing the mount, but it may be useful with
+		non CIFS Unix Extension mounts for cases in which the default
+		mode is specified on the mount but is not to be enforced on the
+		client (e.g. perhaps when MultiUserMount is enabled)
 		Note that this does not affect the normal ACL check on the
 		target machine done by the server software (of the server
 		ACL against the user name provided at mount time).
@@ -368,15 +373,21 @@
   setuids       If the CIFS Unix extensions are negotiated with the server
 		the client will attempt to set the effective uid and gid of
 		the local process on newly created files, directories, and
-		devices (create, mkdir, mknod).
+		devices (create, mkdir, mknod).  If the CIFS Unix Extensions
+		are not negotiated, for newly created files and directories
+		instead of using the default uid and gid specified on the
+		the mount, cache the new file's uid and gid locally which means
+		that the uid for the file can change when the inode is
+	        reloaded (or the user remounts the share).
   nosetuids     The client will not attempt to set the uid and gid on
 		on newly created files, directories, and devices (create, 
 		mkdir, mknod) which will result in the server setting the
 		uid and gid to the default (usually the server uid of the
 		user who mounted the share).  Letting the server (rather than
-		the client) set the uid and gid is the default. This
-		parameter has no effect if the CIFS Unix Extensions are not
-		negotiated.
+		the client) set the uid and gid is the default. If the CIFS
+		Unix Extensions are not negotiated then the uid and gid for
+		new files will appear to be the uid (gid) of the mounter or the
+		uid (gid) parameter specified on the mount.
   netbiosname   When mounting to servers via port 139, specifies the RFC1001
 		source name to use to represent the client netbios machine 
 		name when doing the RFC1001 netbios session initialize.
@@ -418,6 +429,13 @@
 		byte range locks).
  remount        remount the share (often used to change from ro to rw mounts
 	        or vice versa)
+ sfu            When the CIFS Unix Extensions are not negotiated, attempt to
+		create device files and fifos in a format compatible with
+		Services for Unix (SFU).  In addition retrieve bits 10-12
+		of the mode via the SETFILEBITS extended attribute (as
+		SFU does).  In the future the bottom 9 bits of the mode
+		mode also will be emulated using queries of the security
+		descriptor (ACL).
 		
 The mount.cifs mount helper also accepts a few mount options before -o
 including:
diff --git a/fs/cifs/TODO b/fs/cifs/TODO
index c909298..fc34c74 100644
--- a/fs/cifs/TODO
+++ b/fs/cifs/TODO
@@ -1,4 +1,4 @@
-version 1.37 October 9, 2005
+Version 1.39 November 30, 2005
 
 A Partial List of Missing Features
 ==================================
@@ -58,7 +58,7 @@
 at a time when 8 pages or more are requested. In conjuntion
 add support for async_cifs_readpages.
 
-p) Add support for storing symlink and fifo info to Windows servers 
+p) Add support for storing symlink info to Windows servers 
 in the Extended Attribute format their SFU clients would recognize.
 
 q) Finish fcntl D_NOTIFY support so kde and gnome file list windows
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 51548ed..2a13a2b 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->inFlight))
+				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/dir.c b/fs/cifs/dir.c
index 16b2152..32cc96c 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -228,8 +228,15 @@
 		else {
 			rc = cifs_get_inode_info(&newinode, full_path,
 						 buf, inode->i_sb,xid);
-			if(newinode)
+			if(newinode) {
 				newinode->i_mode = mode;
+				if((oplock & CIFS_CREATE_ACTION) &&
+				  (cifs_sb->mnt_cifs_flags & 
+				     CIFS_MOUNT_SET_UID)) {
+					newinode->i_uid = current->fsuid;
+					newinode->i_gid = current->fsgid;
+				}
+			}
 		}
 
 		if (rc != 0) {
@@ -465,12 +472,20 @@
 			direntry->d_op = &cifs_dentry_ops;
 		d_add(direntry, newInode);
 
-		/* since paths are not looked up by component - the parent directories are presumed to be good here */
+		/* since paths are not looked up by component - the parent 
+		   directories are presumed to be good here */
 		renew_parental_timestamps(direntry);
 
 	} else if (rc == -ENOENT) {
 		rc = 0;
+		direntry->d_time = jiffies;
+		if (pTcon->nocase)
+			direntry->d_op = &cifs_ci_dentry_ops;
+		else
+			direntry->d_op = &cifs_dentry_ops;
 		d_add(direntry, NULL);
+	/*	if it was once a directory (but how can we tell?) we could do  
+			shrink_dcache_parent(direntry); */
 	} else {
 		cERROR(1,("Error 0x%x on cifs_get_inode_info in lookup of %s",
 			   rc,full_path));
@@ -489,21 +504,20 @@
 {
 	int isValid = 1;
 
-/*	lock_kernel(); *//* surely we do not want to lock the kernel for a whole network round trip which could take seconds */
-
 	if (direntry->d_inode) {
 		if (cifs_revalidate(direntry)) {
-			/* unlock_kernel(); */
 			return 0;
 		}
 	} else {
-		cFYI(1,
-		     ("In cifs_d_revalidate with no inode but name = %s and dentry 0x%p",
-		      direntry->d_name.name, direntry));
+		cFYI(1, ("neg dentry 0x%p name = %s",
+			 direntry, direntry->d_name.name));
+		if(time_after(jiffies, direntry->d_time + HZ) || 
+			!lookupCacheEnabled) {
+			d_drop(direntry);
+			isValid = 0;
+		} 
 	}
 
-/*    unlock_kernel(); */
-
 	return isValid;
 }
 
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 05b5258..411c1f7 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -710,7 +710,7 @@
 	char *full_path = NULL;
 	struct inode *newinode = NULL;
 
-	cFYI(1, ("In cifs_mkdir, mode = 0x%x inode = 0x%p ", mode, inode));
+	cFYI(1, ("In cifs_mkdir, mode = 0x%x inode = 0x%p", mode, inode));
 
 	xid = GetXid();
 
@@ -768,6 +768,17 @@
 			/* BB to be implemented via Windows secrty descriptors
 			   eg CIFSSMBWinSetPerms(xid, pTcon, full_path, mode,
 						 -1, -1, local_nls); */
+			if(direntry->d_inode) {
+				direntry->d_inode->i_mode = mode;
+				direntry->d_inode->i_mode |= S_IFDIR;
+				if(cifs_sb->mnt_cifs_flags & 
+				     CIFS_MOUNT_SET_UID) {
+					direntry->d_inode->i_uid = 
+						current->fsuid;
+					direntry->d_inode->i_gid = 
+						current->fsgid;
+				}
+			}
 		}
 	}
 	kfree(full_path);
@@ -1039,14 +1050,20 @@
 		filemap_fdatawrite(direntry->d_inode->i_mapping);
 	}
 	if (invalidate_inode) {
-		if (direntry->d_inode->i_mapping)
-			filemap_fdatawait(direntry->d_inode->i_mapping);
-		/* may eventually have to do this for open files too */
-		if (list_empty(&(cifsInode->openFileList))) {
-			/* Has changed on server - flush read ahead pages */
-			cFYI(1, ("Invalidating read ahead data on "
-				 "closed file"));
-			invalidate_remote_inode(direntry->d_inode);
+	/* shrink_dcache not necessary now that cifs dentry ops
+	are exported for negative dentries */
+/*		if(S_ISDIR(direntry->d_inode->i_mode)) 
+			shrink_dcache_parent(direntry); */
+		if (S_ISREG(direntry->d_inode->i_mode)) {
+			if (direntry->d_inode->i_mapping)
+				filemap_fdatawait(direntry->d_inode->i_mapping);
+			/* may eventually have to do this for open files too */
+			if (list_empty(&(cifsInode->openFileList))) {
+				/* changed on server - flush read ahead pages */
+				cFYI(1, ("Invalidating read ahead data on "
+					 "closed file"));
+				invalidate_remote_inode(direntry->d_inode);
+			}
 		}
 	}
 /*	up(&direntry->d_inode->i_sem); */
@@ -1105,9 +1122,20 @@
 
 	cFYI(1, ("In cifs_setattr, name = %s attrs->iavalid 0x%x ",
 		 direntry->d_name.name, attrs->ia_valid));
+
 	cifs_sb = CIFS_SB(direntry->d_inode->i_sb);
 	pTcon = cifs_sb->tcon;
 
+	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) == 0) {
+		/* check if we have permission to change attrs */
+		rc = inode_change_ok(direntry->d_inode, attrs);
+		if(rc < 0) {
+			FreeXid(xid);
+			return rc;
+		} else
+			rc = 0;
+	}
+		
 	down(&direntry->d_sb->s_vfs_rename_sem);
 	full_path = build_path_from_dentry(direntry);
 	up(&direntry->d_sb->s_vfs_rename_sem);
@@ -1147,7 +1175,9 @@
 						  1 /* 45 seconds */);
 				cFYI(1,("Wrt seteof rc %d", rc));
 			}
-		}
+		} else 
+			rc = -EINVAL;
+
 		if (rc != 0) {
 			/* Set file size by pathname rather than by handle
 			   either because no valid, writeable file handle for
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index ca27a82..94baf6c8 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 */