[CIFS] Allow disabling CIFS Unix Extensions as mount option

Previously the only way to do this was to umount all mounts to that server,
turn off a proc setting (/proc/fs/cifs/LinuxExtensionsEnabled).

Fixes Samba bugzilla bug number: 4582 (and also 2008)

Signed-off-by: Steve French <sfrench@us.ibm.com>
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index a0a2f31..1cebb7e 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -316,10 +316,10 @@
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)
 			seq_printf(s, ",posixpaths");
 		if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) ||
-		   !(cifs_sb->tcon->ses->capabilities & CAP_UNIX))
+		   !(cifs_sb->tcon->unix_ext))
 			seq_printf(s, ",uid=%d", cifs_sb->mnt_uid);
 		if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) ||
-		   !(cifs_sb->tcon->ses->capabilities & CAP_UNIX))
+		   !(cifs_sb->tcon->unix_ext))
 			seq_printf(s, ",gid=%d", cifs_sb->mnt_gid);
 		seq_printf(s, ",rsize=%d", cifs_sb->rsize);
 		seq_printf(s, ",wsize=%d", cifs_sb->wsize);
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 0a78131..b98742f 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -281,7 +281,9 @@
 	FILE_SYSTEM_UNIX_INFO fsUnixInfo;
 	unsigned retry:1;
 	unsigned nocase:1;
-	/* BB add field for back pointer to sb struct? */
+	unsigned unix_ext:1; /* if off disable Linux extensions to CIFS protocol
+				for this mount even if server would support */
+	/* BB add field for back pointer to sb struct(s)? */
 };
 
 /*
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index a6ff406..8eb102f 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -132,10 +132,10 @@
 				/* Give Demultiplex thread up to 10 seconds to
 				   reconnect, should be greater than cifs socket
 				   timeout which is 7 seconds */
-			while(tcon->ses->server->tcpStatus ==
+			while (tcon->ses->server->tcpStatus ==
 							 CifsNeedReconnect) {
 				wait_event_interruptible_timeout(tcon->ses->server->response_q,
-					(tcon->ses->server->tcpStatus == 
+					(tcon->ses->server->tcpStatus ==
 							CifsGood), 10 * HZ);
 				if (tcon->ses->server->tcpStatus ==
 							CifsNeedReconnect) {
@@ -213,7 +213,7 @@
 	}
 
 	header_assemble((struct smb_hdr *) *request_buf, smb_command,
-			tcon,wct);
+			tcon, wct);
 
 	if (tcon != NULL)
 		cifs_stats_inc(&tcon->num_smbs_sent);
@@ -387,7 +387,7 @@
 			/* check that bcc is less than negotiated smb buffer */
 			total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount);
 			if (total_size < 512) {
-				total_size += 
+				total_size +=
 					le16_to_cpu(pSMB->t2_rsp.DataCount);
 				/* BCC le converted in SendReceive */
 				pBCC = (pSMB->hdr.WordCount * 2) +
@@ -2758,7 +2758,7 @@
 		return 0;
 
 	count = posix_acl_xattr_count((size_t)buflen);
-	cFYI(1,("setting acl with %d entries from buf of length %d and "
+	cFYI(1, ("setting acl with %d entries from buf of length %d and "
 		"version of %d",
 		count, buflen, le32_to_cpu(local_acl->a_version)));
 	if (le32_to_cpu(local_acl->a_version) != 2) {
@@ -3638,15 +3638,6 @@
 	pSMB->SearchHandle = searchHandle;      /* always kept as le */
 	pSMB->SearchCount =
 		cpu_to_le16(CIFSMaxBufSize / sizeof (FILE_UNIX_INFO));
-	/* test for Unix extensions */
-/*	if (tcon->ses->capabilities & CAP_UNIX) {
-		pSMB->InformationLevel = cpu_to_le16(SMB_FIND_FILE_UNIX);
-		psrch_inf->info_level = SMB_FIND_FILE_UNIX;
-	} else {
-		pSMB->InformationLevel =
-		   cpu_to_le16(SMB_FIND_FILE_DIRECTORY_INFO);
-		psrch_inf->info_level = SMB_FIND_FILE_DIRECTORY_INFO;
-	} */
 	pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
 	pSMB->ResumeKey = psrch_inf->resume_key;
 	pSMB->SearchFlags =
@@ -3966,7 +3957,7 @@
 					(8 /* sizeof start of data block */ +
 					data_offset +
 					(char *) &pSMBr->hdr.Protocol);
-			cFYI(1,("num_referrals: %d dfs flags: 0x%x ... \n"
+			cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n"
 				"for referral one refer size: 0x%x srv "
 				"type: 0x%x refer flags: 0x%x ttl: 0x%x",
 				le16_to_cpu(pSMBr->NumberOfReferrals),
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 8b341a8..e93da7a 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -85,6 +85,7 @@
 	unsigned direct_io:1;
 	unsigned remap:1;   /* set to remap seven reserved chars in filenames */
 	unsigned posix_paths:1;   /* unset to not ask for posix pathnames. */
+	unsigned no_linux_ext:1;
 	unsigned sfu_emul:1;
 	unsigned nullauth:1; /* attempt to authenticate with null user */
 	unsigned nocase;     /* request case insensitive filenames */
@@ -1192,6 +1193,10 @@
 			vol->posix_paths = 1;
 		} else if (strnicmp(data, "noposixpaths", 12) == 0) {
 			vol->posix_paths = 0;
+		} else if (strnicmp(data, "nounix", 6) == 0) {
+			vol->no_linux_ext = 1;
+		} else if (strnicmp(data, "nolinux", 7) == 0) {
+			vol->no_linux_ext = 1;
 		} else if ((strnicmp(data, "nocase", 6) == 0) ||
 			   (strnicmp(data, "ignorecase", 10)  == 0)) {
 			vol->nocase = 1;
@@ -1665,6 +1670,18 @@
 	 * and once without posixacls or posix paths? */
 	__u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
 
+	if (vol_info && vol_info->no_linux_ext) {
+		tcon->fsUnixInfo.Capability = 0;
+		tcon->unix_ext = 0; /* Unix Extensions disabled */
+		cFYI(1, ("Linux protocol extensions disabled"));
+		return;
+	} else if (vol_info)
+		tcon->unix_ext = 1; /* Unix Extensions supported */
+
+	if (tcon->unix_ext == 0) {
+		cFYI(1, ("Unix extensions disabled so not set on reconnect"));
+		return;
+	}
 
 	if (!CIFSSMBQFSUnixInfo(xid, tcon)) {
 		__u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
@@ -1678,10 +1695,6 @@
 				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;
@@ -2176,13 +2189,17 @@
 
 		/* tell server which Unix caps we support */
 		if (tcon->ses->capabilities & CAP_UNIX)
+			/* reset of caps checks mount to see if unix extensions
+			   disabled for just this mount */
 			reset_cifs_unix_caps(xid, tcon, sb, &volume_info);
-		else if (cifs_sb->rsize > (1024 * 127)) {
+		else
+			tcon->unix_ext = 0; /* server does not support them */
+
+		if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) {
 			cifs_sb->rsize = 1024 * 127;
 #ifdef CONFIG_CIFS_DEBUG2
-			cFYI(1, ("no very large read support, rsize 127K"));
+			cFYI(1, ("no very large read support, rsize now 127K"));
 #endif
-
 		}
 		if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
 			cifs_sb->wsize = min(cifs_sb->wsize,
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index def89f2..4830acc 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -207,8 +207,7 @@
 	} else {
 		/* If Open reported that we actually created a file
 		then we now have to set the mode if possible */
-		if ((cifs_sb->tcon->ses->capabilities & CAP_UNIX) &&
-			(oplock & CIFS_CREATE_ACTION)) {
+		if ((pTcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) {
 			mode &= ~current->fs->umask;
 			if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
 				CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode,
@@ -235,8 +234,8 @@
 			/* Could set r/o dos attribute if mode & 0222 == 0 */
 		}
 
-	/* BB server might mask mode so we have to query for Unix case*/
-		if (pTcon->ses->capabilities & CAP_UNIX)
+		/* server might mask mode so we have to query for it */
+		if (pTcon->unix_ext)
 			rc = cifs_get_inode_info_unix(&newinode, full_path,
 						 inode->i_sb, xid);
 		else {
@@ -337,7 +336,7 @@
 	full_path = build_path_from_dentry(direntry);
 	if (full_path == NULL)
 		rc = -ENOMEM;
-	else if (pTcon->ses->capabilities & CAP_UNIX) {
+	else if (pTcon->unix_ext) {
 		mode &= ~current->fs->umask;
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
 			rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path,
@@ -491,7 +490,7 @@
 	cFYI(1,
 	     (" Full path: %s inode = 0x%p", full_path, direntry->d_inode));
 
-	if (pTcon->ses->capabilities & CAP_UNIX)
+	if (pTcon->unix_ext)
 		rc = cifs_get_inode_info_unix(&newInode, full_path,
 					      parent_dir_inode->i_sb, xid);
 	else
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 0a39491..e13592a 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -138,7 +138,7 @@
 	}
 
 client_can_cache:
-	if (pTcon->ses->capabilities & CAP_UNIX)
+	if (pTcon->unix_ext)
 		rc = cifs_get_inode_info_unix(&file->f_path.dentry->d_inode,
 			full_path, inode->i_sb, xid);
 	else
@@ -303,7 +303,7 @@
 	if (oplock & CIFS_CREATE_ACTION) {
 		/* time to set mode which we can not set earlier due to
 		   problems creating new read-only files */
-		if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) {
+		if (pTcon->unix_ext) {
 			CIFSSMBUnixSetPerms(xid, pTcon, full_path,
 					    inode->i_mode,
 					    (__u64)-1, (__u64)-1, 0 /* dev */,
@@ -430,7 +430,7 @@
 			   go to server to get inode info */
 				pCifsInode->clientCanCacheAll = FALSE;
 				pCifsInode->clientCanCacheRead = FALSE;
-				if (pTcon->ses->capabilities & CAP_UNIX)
+				if (pTcon->unix_ext)
 					rc = cifs_get_inode_info_unix(&inode,
 						full_path, inode->i_sb, xid);
 				else
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 3482879..dd41677 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -583,7 +583,8 @@
 
 	cifs_sb = CIFS_SB(inode->i_sb);
 	xid = GetXid();
-	if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
+
+	if (cifs_sb->tcon->unix_ext)
 		cifs_get_inode_info_unix(&inode, "", inode->i_sb, xid);
 	else
 		cifs_get_inode_info(&inode, "", NULL, inode->i_sb, xid);
@@ -981,7 +982,7 @@
 	} else {
 mkdir_get_info:
 		inc_nlink(inode);
-		if (pTcon->ses->capabilities & CAP_UNIX)
+		if (pTcon->unix_ext)
 			rc = cifs_get_inode_info_unix(&newinode, full_path,
 						      inode->i_sb, xid);
 		else
@@ -997,7 +998,7 @@
 		  * failed to get it from the server or was set bogus */
 		if ((direntry->d_inode) && (direntry->d_inode->i_nlink < 2))
 				direntry->d_inode->i_nlink = 2;
-		if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) {
+		if (pTcon->unix_ext) {
 			mode &= ~current->fs->umask;
 			if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
 				CIFSSMBUnixSetPerms(xid, pTcon, full_path,
@@ -1130,7 +1131,7 @@
 			kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
 		if (info_buf_source != NULL) {
 			info_buf_target = info_buf_source + 1;
-			if (pTcon->ses->capabilities & CAP_UNIX)
+			if (pTcon->unix_ext)
 				rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName,
 					info_buf_source,
 					cifs_sb_source->local_nls,
@@ -1258,7 +1259,7 @@
 	local_mtime = direntry->d_inode->i_mtime;
 	local_size = direntry->d_inode->i_size;
 
-	if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) {
+	if (cifs_sb->tcon->unix_ext) {
 		rc = cifs_get_inode_info_unix(&direntry->d_inode, full_path,
 					      direntry->d_sb, xid);
 		if (rc) {
@@ -1542,7 +1543,7 @@
 		mode = attrs->ia_mode;
 	}
 
-	if ((cifs_sb->tcon->ses->capabilities & CAP_UNIX)
+	if ((pTcon->unix_ext)
 	    && (attrs->ia_valid & (ATTR_MODE | ATTR_GID | ATTR_UID)))
 		rc = CIFSSMBUnixSetPerms(xid, pTcon, full_path, mode, uid, gid,
 					 0 /* dev_t */, cifs_sb->local_nls,
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index 7da755c..6a85ef7 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -55,7 +55,8 @@
 		goto cifs_hl_exit;
 	}
 
-	if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)
+/*	if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/
+	if (pTcon->unix_ext)
 		rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName,
 					    cifs_sb_target->local_nls,
 					    cifs_sb_target->mnt_cifs_flags &
@@ -129,14 +130,19 @@
 		goto out;
 	}
 
-	/* BB add read reparse point symlink code and Unix extensions
-	   symlink code here BB */
+	/* We could change this to:
+		if (pTcon->unix_ext)
+	   but there does not seem any point in refusing to
+	   get symlink info if we can, even if unix extensions
+	   turned off for this mount */
+
 	if (pTcon->ses->capabilities & CAP_UNIX)
 		rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path,
 					     target_path,
 					     PATH_MAX-1,
 					     cifs_sb->local_nls);
 	else {
+		/* BB add read reparse point symlink code here */
 		/* rc = CIFSSMBQueryReparseLinkInfo */
 		/* BB Add code to Query ReparsePoint info */
 		/* BB Add MAC style xsymlink check here if enabled */
@@ -186,7 +192,7 @@
 	cFYI(1, ("symname is %s", symname));
 
 	/* BB what if DFS and this volume is on different share? BB */
-	if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
+	if (pTcon->unix_ext)
 		rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
 					   cifs_sb->local_nls);
 	/* else
@@ -194,7 +200,7 @@
 					cifs_sb_target->local_nls); */
 
 	if (rc == 0) {
-		if (pTcon->ses->capabilities & CAP_UNIX)
+		if (pTcon->unix_ext)
 			rc = cifs_get_inode_info_unix(&newinode, full_path,
 						      inode->i_sb, xid);
 		else
@@ -266,6 +272,7 @@
 
 /* BB add read reparse point symlink code and
 	Unix extensions symlink code here BB */
+/* We could disable this based on pTcon->unix_ext flag instead ... but why? */
 	if (cifs_sb->tcon->ses->capabilities & CAP_UNIX)
 		rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path,
 				tmpbuffer,
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 07f9253..916df94 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -463,7 +463,9 @@
 
 ffirst_retry:
 	/* test for Unix extensions */
-	if (pTcon->ses->capabilities & CAP_UNIX) {
+	/* but now check for them on the share/mount not on the SMB session */
+/*	if (pTcon->ses->capabilities & CAP_UNIX) { */
+	if (pTcon->unix_ext) {
 		cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
 	} else if ((pTcon->ses->capabilities &
 			(CAP_NT_SMBS | CAP_NT_FIND)) == 0) {