Bladeren bron

[CIFS] on reconnect to Samba - reset the unix capabilities

After temporary server or network failure and reconneciton, we were not
resending the unix capabilities via SetFSInfo - which confused Samba posix
byte range locking code.

Discovered by jra

Signed-off-by: Steve French <sfrench@us.ibm.com>
Steve French 18 jaren geleden
bovenliggende
commit
8af1897158
8 gewijzigde bestanden met toevoegingen van 130 en 67 verwijderingen
  1. 11 18
      fs/Kconfig
  2. 6 1
      fs/cifs/CHANGES
  3. 8 0
      fs/cifs/TODO
  4. 1 1
      fs/cifs/cifsfs.h
  5. 1 1
      fs/cifs/cifspdu.h
  6. 3 0
      fs/cifs/cifsproto.h
  7. 14 2
      fs/cifs/cifssmb.c
  8. 86 44
      fs/cifs/connect.c

+ 11 - 18
fs/Kconfig

@@ -1871,20 +1871,14 @@ config CIFS
 	  file servers such as Windows 2000 (including Windows 2003, NT 4  
 	  file servers such as Windows 2000 (including Windows 2003, NT 4  
 	  and Windows XP) as well by Samba (which provides excellent CIFS
 	  and Windows XP) as well by Samba (which provides excellent CIFS
 	  server support for Linux and many other operating systems). Limited
 	  server support for Linux and many other operating systems). Limited
-	  support for Windows ME and similar servers is provided as well. 
-	  You must use the smbfs client filesystem to access older SMB servers
-	  such as OS/2 and DOS.
+	  support for OS/2 and Windows ME and similar servers is provided as well.
 
 
 	  The intent of the cifs module is to provide an advanced
 	  The intent of the cifs module is to provide an advanced
-	  network file system client for mounting to CIFS compliant servers, 
+	  network file system client for mounting to CIFS compliant servers,
 	  including support for dfs (hierarchical name space), secure per-user
 	  including support for dfs (hierarchical name space), secure per-user
 	  session establishment, safe distributed caching (oplock), optional
 	  session establishment, safe distributed caching (oplock), optional
-	  packet signing, Unicode and other internationalization improvements, 
-	  and optional Winbind (nsswitch) integration. You do not need to enable
-	  cifs if running only a (Samba) server. It is possible to enable both
-	  smbfs and cifs (e.g. if you are using CIFS for accessing Windows 2003
-	  and Samba 3 servers, and smbfs for accessing old servers). If you need 
-	  to mount to Samba or Windows from this machine, say Y.
+	  packet signing, Unicode and other internationalization improvements. 
+	  If you need to mount to Samba or Windows from this machine, say Y.
 
 
 config CIFS_STATS
 config CIFS_STATS
         bool "CIFS statistics"
         bool "CIFS statistics"
@@ -1977,14 +1971,13 @@ config CIFS_EXPERIMENTAL
 	  depends on CIFS && EXPERIMENTAL
 	  depends on CIFS && EXPERIMENTAL
 	  help
 	  help
 	    Enables cifs features under testing. These features are
 	    Enables cifs features under testing. These features are
-	    experimental and currently include support for writepages
-	    (multipage writebehind performance improvements) and directory
-	    change notification ie fcntl(F_DNOTIFY) as well as some security
-	    improvements.  Some also depend on setting at runtime the
-	    pseudo-file /proc/fs/cifs/Experimental (which is disabled by
-	    default). See the file fs/cifs/README for more details.
-
-	    If unsure, say N.
+	    experimental and currently include DFS support and directory 
+	    change notification ie fcntl(F_DNOTIFY), as well as the upcall
+	    mechanism which will be used for Kerberos session negotiation
+	    and uid remapping.  Some of these features also may depend on 
+	    setting a value of 1 to the pseudo-file /proc/fs/cifs/Experimental
+	    (which is disabled by default). See the file fs/cifs/README 
+	    for more details.  If unsure, say N.
 
 
 config CIFS_UPCALL
 config CIFS_UPCALL
 	  bool "Kerberos/SPNEGO advanced session setup (EXPERIMENTAL)"
 	  bool "Kerberos/SPNEGO advanced session setup (EXPERIMENTAL)"

+ 6 - 1
fs/cifs/CHANGES

@@ -4,7 +4,12 @@ Fix oops in list_del during mount caused by unaligned string.
 Fix file corruption which could occur on some large file
 Fix file corruption which could occur on some large file
 copies caused by writepages page i/o completion bug.
 copies caused by writepages page i/o completion bug.
 Seek to SEEK_END forces check for update of file size for non-cached
 Seek to SEEK_END forces check for update of file size for non-cached
-files.
+files. Allow file size to be updated on remote extend of locally open,
+non-cached file.  Fix reconnect to newer Samba servers (or other servers
+which support the CIFS Unix/POSIX extensions) so that we again tell the
+server the Unix/POSIX cifs capabilities which we support (SetFSInfo).
+Add experimental support for new POSIX Open/Mkdir (which returns
+stat information on the open, and allows setting the mode).
 
 
 Version 1.46
 Version 1.46
 ------------
 ------------

+ 8 - 0
fs/cifs/TODO

@@ -128,3 +128,11 @@ negotiated size) and send larger write sizes to modern servers.
 
 
 4) More exhaustively test against less common servers.  More testing
 4) More exhaustively test against less common servers.  More testing
 against Windows 9x, Windows ME servers.
 against Windows 9x, Windows ME servers.
+
+DOS attrs - returned as pseudo-xattr in Samba format (check VFAT and NTFS for this too)
+
+mount check for unmatched uids - and uid override
+
+Add mount option for Linux extension disable per mount, and partial disable per mount (uid off, symlink/fifo/mknod on but what about posix acls?) 
+
+Free threads at umount --force that are stuck on the sesSem

+ 1 - 1
fs/cifs/cifsfs.h

@@ -100,5 +100,5 @@ extern ssize_t	cifs_getxattr(struct dentry *, const char *, void *, size_t);
 extern ssize_t	cifs_listxattr(struct dentry *, char *, size_t);
 extern ssize_t	cifs_listxattr(struct dentry *, char *, size_t);
 extern int cifs_ioctl (struct inode * inode, struct file * filep,
 extern int cifs_ioctl (struct inode * inode, struct file * filep,
 		       unsigned int command, unsigned long arg);
 		       unsigned int command, unsigned long arg);
-#define CIFS_VERSION   "1.47"
+#define CIFS_VERSION   "1.48"
 #endif				/* _CIFSFS_H */
 #endif				/* _CIFSFS_H */

+ 1 - 1
fs/cifs/cifspdu.h

@@ -2108,7 +2108,7 @@ typedef struct {
 
 
 typedef struct {
 typedef struct {
 	/* reply varies based on requested level */
 	/* reply varies based on requested level */
-} __atribute__((packed)) OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */
+} __attribute__((packed)) OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */
 
 
 
 
 struct file_internal_info {
 struct file_internal_info {

+ 3 - 0
fs/cifs/cifsproto.h

@@ -23,6 +23,7 @@
 #include <linux/nls.h>
 #include <linux/nls.h>
 
 
 struct statfs;
 struct statfs;
+struct smb_vol;
 
 
 /*
 /*
  *****************************************************************
  *****************************************************************
@@ -147,6 +148,8 @@ extern int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
 			unsigned int *pnum_referrals, 
 			unsigned int *pnum_referrals, 
 			unsigned char ** preferrals,
 			unsigned char ** preferrals,
 			int remap);
 			int remap);
+extern void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon,
+				 struct super_block * sb, struct smb_vol * vol);
 extern int CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon,
 extern int CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon,
 			struct kstatfs *FSData);
 			struct kstatfs *FSData);
 extern int SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon,
 extern int SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon,

+ 14 - 2
fs/cifs/cifssmb.c

@@ -158,9 +158,15 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
 							nls_codepage);
 							nls_codepage);
 			if(!rc && (tcon->tidStatus == CifsNeedReconnect)) {
 			if(!rc && (tcon->tidStatus == CifsNeedReconnect)) {
 				mark_open_files_invalid(tcon);
 				mark_open_files_invalid(tcon);
-				rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon
-					, nls_codepage);
+				rc = CIFSTCon(0, tcon->ses, tcon->treeName, 
+					      tcon, nls_codepage);
 				up(&tcon->ses->sesSem);
 				up(&tcon->ses->sesSem);
+				/* tell server which Unix caps we support */
+				if (tcon->ses->capabilities & CAP_UNIX)
+					reset_cifs_unix_caps(0 /* no xid */,
+						tcon, 
+						NULL /* we do not know sb */,
+						NULL /* no vol info */);	
 				/* BB FIXME add code to check if wsize needs
 				/* BB FIXME add code to check if wsize needs
 				   update due to negotiated smb buffer size
 				   update due to negotiated smb buffer size
 				   shrinking */
 				   shrinking */
@@ -298,6 +304,12 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
 				rc = CIFSTCon(0, tcon->ses, tcon->treeName,
 				rc = CIFSTCon(0, tcon->ses, tcon->treeName,
 					      tcon, nls_codepage);
 					      tcon, nls_codepage);
 				up(&tcon->ses->sesSem);
 				up(&tcon->ses->sesSem);
+				/* tell server which Unix caps we support */
+				if (tcon->ses->capabilities & CAP_UNIX)
+					reset_cifs_unix_caps(0 /* no xid */,
+						tcon, 
+						NULL /* do not know sb */,
+						NULL /* no vol info */);
 				/* BB FIXME add code to check if wsize needs
 				/* BB FIXME add code to check if wsize needs
 				update due to negotiated smb buffer size
 				update due to negotiated smb buffer size
 				shrinking */
 				shrinking */

+ 86 - 44
fs/cifs/connect.c

@@ -1613,6 +1613,76 @@ ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket)
 	return rc;
 	return rc;
 }
 }
 
 
+void reset_cifs_unix_caps(int xid, struct cifsTconInfo * tcon, 
+			  struct super_block * sb, struct smb_vol * vol_info)
+{
+	/* if we are reconnecting then should we check to see if
+	 * any requested capabilities changed locally e.g. via
+	 * remount but we can not do much about it here
+	 * if they have (even if we could detect it by the following)
+	 * Perhaps we could add a backpointer to array of sb from tcon
+	 * or if we change to make all sb to same share the same
+	 * sb as NFS - then we only have one backpointer to sb.
+	 * What if we wanted to mount the server share twice once with
+	 * and once without posixacls or posix paths? */
+	__u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
+	   
+	 
+	if(!CIFSSMBQFSUnixInfo(xid, tcon)) {
+		__u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
+		
+		/* check for reconnect case in which we do not
+		   want to change the mount behavior if we can avoid it */
+		if(vol_info == NULL) {
+			/* turn off POSIX ACL and PATHNAMES if not set 
+			   originally at mount time */
+			if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0)
+				cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
+			if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0)
+				cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
+				
+
+			 
+			
+		}
+		
+		cap &= CIFS_UNIX_CAP_MASK;
+		if(vol_info && vol_info->no_psx_acl)
+			cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
+		else if(CIFS_UNIX_POSIX_ACL_CAP & cap) {
+			cFYI(1,("negotiated posix acl support"));
+			if(sb)
+				sb->s_flags |= MS_POSIXACL;
+		}
+
+		if(vol_info && vol_info->posix_paths == 0)
+			cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
+		else if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
+			cFYI(1,("negotiate posix pathnames"));
+			if(sb)
+				CIFS_SB(sb)->mnt_cifs_flags |= 
+					CIFS_MOUNT_POSIX_PATHS;
+		}
+			
+		cFYI(1,("Negotiate caps 0x%x",(int)cap));
+#ifdef CONFIG_CIFS_DEBUG2
+		if(cap & CIFS_UNIX_FCNTL_CAP)
+			cFYI(1,("FCNTL cap"));
+		if(cap & CIFS_UNIX_EXTATTR_CAP)
+			cFYI(1,("EXTATTR cap"));
+		if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
+			cFYI(1,("POSIX path cap"));
+		if(cap & CIFS_UNIX_XATTR_CAP)
+			cFYI(1,("XATTR cap"));
+		if(cap & CIFS_UNIX_POSIX_ACL_CAP)
+			cFYI(1,("POSIX ACL cap"));
+#endif /* CIFS_DEBUG2 */
+		if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) {
+			cFYI(1,("setting capabilities failed"));
+		}
+	}
+}
+
 int
 int
 cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 	   char *mount_data, const char *devname)
 	   char *mount_data, const char *devname)
@@ -1928,20 +1998,25 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 			if (tcon == NULL)
 			if (tcon == NULL)
 				rc = -ENOMEM;
 				rc = -ENOMEM;
 			else {
 			else {
-				/* check for null share name ie connect to dfs root */
+				/* check for null share name ie connecting to 
+				 * dfs root */
 
 
-				/* BB check if this works for exactly length three strings */
+				/* BB check if this works for exactly length 
+				 * three strings */
 				if ((strchr(volume_info.UNC + 3, '\\') == NULL)
 				if ((strchr(volume_info.UNC + 3, '\\') == NULL)
 				    && (strchr(volume_info.UNC + 3, '/') ==
 				    && (strchr(volume_info.UNC + 3, '/') ==
 					NULL)) {
 					NULL)) {
 					rc = connect_to_dfs_path(xid, pSesInfo,
 					rc = connect_to_dfs_path(xid, pSesInfo,
-							"", cifs_sb->local_nls,
-							cifs_sb->mnt_cifs_flags & 
-							  CIFS_MOUNT_MAP_SPECIAL_CHR);
+						"", cifs_sb->local_nls,
+						cifs_sb->mnt_cifs_flags & 
+						  CIFS_MOUNT_MAP_SPECIAL_CHR);
 					kfree(volume_info.UNC);
 					kfree(volume_info.UNC);
 					FreeXid(xid);
 					FreeXid(xid);
 					return -ENODEV;
 					return -ENODEV;
 				} else {
 				} else {
+					/* BB Do we need to wrap sesSem around
+					 * this TCon call and Unix SetFS as
+					 * we do on SessSetup and reconnect? */
 					rc = CIFSTCon(xid, pSesInfo, 
 					rc = CIFSTCon(xid, pSesInfo, 
 						volume_info.UNC,
 						volume_info.UNC,
 						tcon, cifs_sb->local_nls);
 						tcon, cifs_sb->local_nls);
@@ -1962,6 +2037,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 			sb->s_maxbytes = (u64) 1 << 31;	/* 2 GB */
 			sb->s_maxbytes = (u64) 1 << 31;	/* 2 GB */
 	}
 	}
 
 
+	/* BB FIXME fix time_gran to be larger for LANMAN sessions */
 	sb->s_time_gran = 100;
 	sb->s_time_gran = 100;
 
 
 /* on error free sesinfo and tcon struct if needed */
 /* on error free sesinfo and tcon struct if needed */
@@ -2006,45 +2082,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 		/* do not care if following two calls succeed - informational */
 		/* do not care if following two calls succeed - informational */
 		CIFSSMBQFSDeviceInfo(xid, tcon);
 		CIFSSMBQFSDeviceInfo(xid, tcon);
 		CIFSSMBQFSAttributeInfo(xid, tcon);
 		CIFSSMBQFSAttributeInfo(xid, tcon);
-
-		if (tcon->ses->capabilities & CAP_UNIX) {
-			if(!CIFSSMBQFSUnixInfo(xid, tcon)) {
-				__u64 cap = 
-				       le64_to_cpu(tcon->fsUnixInfo.Capability);
-				cap &= CIFS_UNIX_CAP_MASK;
-				if(volume_info.no_psx_acl)
-					cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
-				else if(CIFS_UNIX_POSIX_ACL_CAP & cap) {
-					cFYI(1,("negotiated posix acl support"));
-					sb->s_flags |= MS_POSIXACL;
-				}
-
-				if(volume_info.posix_paths == 0)
-					cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
-				else if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
-					cFYI(1,("negotiate posix pathnames"));
-					cifs_sb->mnt_cifs_flags |= 
-						CIFS_MOUNT_POSIX_PATHS;
-				}
-					
-				cFYI(1,("Negotiate caps 0x%x",(int)cap));
-#ifdef CONFIG_CIFS_DEBUG2
-				if(cap & CIFS_UNIX_FCNTL_CAP)
-					cFYI(1,("FCNTL cap"));
-				if(cap & CIFS_UNIX_EXTATTR_CAP)
-					cFYI(1,("EXTATTR cap"));
-				if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
-					cFYI(1,("POSIX path cap"));
-				if(cap & CIFS_UNIX_XATTR_CAP)
-					cFYI(1,("XATTR cap"));
-				if(cap & CIFS_UNIX_POSIX_ACL_CAP)
-					cFYI(1,("POSIX ACL cap"));
-#endif /* CIFS_DEBUG2 */
-				if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) {
-					cFYI(1,("setting capabilities failed"));
-				}
-			}
-		}
+		
+		/* tell server which Unix caps we support */
+		if (tcon->ses->capabilities & CAP_UNIX)
+			reset_cifs_unix_caps(xid, tcon, sb, &volume_info);
+		
 		if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
 		if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
 			cifs_sb->wsize = min(cifs_sb->wsize,
 			cifs_sb->wsize = min(cifs_sb->wsize,
 					     (tcon->ses->server->maxBuf -
 					     (tcon->ses->server->maxBuf -