From 8af18971584d1e05770560206cfdfd1d6ba8a17f Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 14 Feb 2007 04:42:51 +0000 Subject: [PATCH] [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 --- fs/Kconfig | 29 ++++------ fs/cifs/CHANGES | 7 ++- fs/cifs/TODO | 8 +++ fs/cifs/cifsfs.h | 2 +- fs/cifs/cifspdu.h | 2 +- fs/cifs/cifsproto.h | 3 + fs/cifs/cifssmb.c | 16 +++++- fs/cifs/connect.c | 130 +++++++++++++++++++++++++++++--------------- 8 files changed, 130 insertions(+), 67 deletions(-) diff --git a/fs/Kconfig b/fs/Kconfig index 8cd2417a14..d57f363ea1 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -1871,20 +1871,14 @@ config CIFS file servers such as Windows 2000 (including Windows 2003, NT 4 and Windows XP) as well by Samba (which provides excellent CIFS 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 - 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 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 bool "CIFS statistics" @@ -1977,14 +1971,13 @@ config CIFS_EXPERIMENTAL depends on CIFS && EXPERIMENTAL help 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 bool "Kerberos/SPNEGO advanced session setup (EXPERIMENTAL)" diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index a1fb03f196..5fe13593b5 100644 --- a/fs/cifs/CHANGES +++ b/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 copies caused by writepages page i/o completion bug. 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 ------------ diff --git a/fs/cifs/TODO b/fs/cifs/TODO index fc34c74ec4..68372946dc 100644 --- a/fs/cifs/TODO +++ b/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 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 diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 8aa66dcf13..e36b0d43e9 100644 --- a/fs/cifs/cifsfs.h +++ b/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 int cifs_ioctl (struct inode * inode, struct file * filep, unsigned int command, unsigned long arg); -#define CIFS_VERSION "1.47" +#define CIFS_VERSION "1.48" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 529a000bee..7d9505491b 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -2108,7 +2108,7 @@ typedef struct { typedef struct { /* 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 { diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 1108f17bf5..6148b82170 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -23,6 +23,7 @@ #include struct statfs; +struct smb_vol; /* ***************************************************************** @@ -147,6 +148,8 @@ extern int get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, unsigned int *pnum_referrals, unsigned char ** preferrals, 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, struct kstatfs *FSData); extern int SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 472e33e0f3..b8e91470c2 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -158,9 +158,15 @@ small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, nls_codepage); if(!rc && (tcon->tidStatus == CifsNeedReconnect)) { 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); + /* 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 update due to negotiated smb buffer size shrinking */ @@ -298,6 +304,12 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon, rc = CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); 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 update due to negotiated smb buffer size shrinking */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 2caca06b4b..20ba7dcc99 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1613,6 +1613,76 @@ ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket) 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 cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, 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) rc = -ENOMEM; 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) && (strchr(volume_info.UNC + 3, '/') == NULL)) { 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); FreeXid(xid); return -ENODEV; } 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, volume_info.UNC, 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 */ } + /* BB FIXME fix time_gran to be larger for LANMAN sessions */ sb->s_time_gran = 100; /* 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 */ CIFSSMBQFSDeviceInfo(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)) cifs_sb->wsize = min(cifs_sb->wsize, (tcon->ses->server->maxBuf - -- 2.39.5