From c18c842b1fdf527717303a4e173cbece7ab2deb8 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 18 Jul 2007 23:21:09 +0000 Subject: [PATCH] [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 --- fs/cifs/cifsfs.c | 4 ++-- fs/cifs/cifsglob.h | 4 +++- fs/cifs/cifssmb.c | 21 ++++++--------------- fs/cifs/connect.c | 31 ++++++++++++++++++++++++------- fs/cifs/dir.c | 11 +++++------ fs/cifs/file.c | 6 +++--- fs/cifs/inode.c | 13 +++++++------ fs/cifs/link.c | 17 ++++++++++++----- fs/cifs/readdir.c | 4 +++- 9 files changed, 65 insertions(+), 46 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index a0a2f3186557..1cebb7e34215 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -316,10 +316,10 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m) 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 0a7813175a27..b98742fc3b5a 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -281,7 +281,9 @@ struct cifsTconInfo { 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 a6ff406ac6b4..8eb102f940d4 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -132,10 +132,10 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, /* 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 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, } 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 @@ static int validate_t2(struct smb_t2_rsp *pSMB) /* 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 @@ static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL, 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 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, 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 @@ getDFSRetry: (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 8b341a8599f8..e93da7ad9002 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -85,6 +85,7 @@ struct smb_vol { 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 @@ cifs_parse_mount_options(char *options, const char *devname, 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 @@ void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon, * 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 @@ void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon, 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 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, /* 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 def89f23fe55..4830acc86d74 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -207,8 +207,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, } 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 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, /* 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 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, 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 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, 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 0a39491280d1..e13592afca9c 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -138,7 +138,7 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file, } 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 @@ int cifs_open(struct inode *inode, struct file *file) 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 @@ reopen_error_exit: 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 3482879b3d3f..dd4167762a8e 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -583,7 +583,8 @@ void cifs_read_inode(struct inode *inode) 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 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) } 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 @@ mkdir_get_info: * 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 @@ int cifs_rename(struct inode *source_inode, struct dentry *source_direntry, 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 @@ int cifs_revalidate(struct dentry *direntry) 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 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) 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 7da755c6550a..6a85ef7b8797 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -55,7 +55,8 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, 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 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) 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 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) 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_symlink(struct inode *inode, struct dentry *direntry, const char *symname) 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 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen) /* 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 07f92531f74f..916df9431336 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -463,7 +463,9 @@ static int initiate_cifs_search(const int xid, struct file *file) 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) { -- 2.39.2