提交 3d694380 编写于 作者: J Jeff Layton 提交者: Steve French

cifs: guard against hardlinking directories

When we made serverino the default, we trusted that the field sent by the
server in the "uniqueid" field was actually unique. It turns out that it
isn't reliably so.

Samba, in particular, will just put the st_ino in the uniqueid field when
unix extensions are enabled. When a share spans multiple filesystems, it's
quite possible that there will be collisions. This is a server bug, but
when the inodes in question are a directory (as is often the case) and
there is a collision with the root inode of the mount, the result is a
kernel panic on umount.

Fix this by checking explicitly for directory inodes with the same
uniqueid. If that is the case, then we can assume that using server inode
numbers will be a problem and that they should be disabled.

Fixes Samba bugzilla 7407
Signed-off-by: NJeff Layton <jlayton@redhat.com>
CC: Stable <stable@kernel.org>
Reviewed-and-Tested-by: NSuresh Jayaraman <sjayaraman@suse.de>
Signed-off-by: NSteve French <sfrench@us.ibm.com>
上级 05ce7bfe
...@@ -502,6 +502,7 @@ struct dfs_info3_param { ...@@ -502,6 +502,7 @@ struct dfs_info3_param {
#define CIFS_FATTR_DFS_REFERRAL 0x1 #define CIFS_FATTR_DFS_REFERRAL 0x1
#define CIFS_FATTR_DELETE_PENDING 0x2 #define CIFS_FATTR_DELETE_PENDING 0x2
#define CIFS_FATTR_NEED_REVAL 0x4 #define CIFS_FATTR_NEED_REVAL 0x4
#define CIFS_FATTR_INO_COLLISION 0x8
struct cifs_fattr { struct cifs_fattr {
u32 cf_flags; u32 cf_flags;
......
...@@ -715,6 +715,16 @@ cifs_find_inode(struct inode *inode, void *opaque) ...@@ -715,6 +715,16 @@ cifs_find_inode(struct inode *inode, void *opaque)
if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid) if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid)
return 0; return 0;
/*
* uh oh -- it's a directory. We can't use it since hardlinked dirs are
* verboten. Disable serverino and return it as if it were found, the
* caller can discard it, generate a uniqueid and retry the find
*/
if (S_ISDIR(inode->i_mode) && !list_empty(&inode->i_dentry)) {
fattr->cf_flags |= CIFS_FATTR_INO_COLLISION;
cifs_autodisable_serverino(CIFS_SB(inode->i_sb));
}
return 1; return 1;
} }
...@@ -734,15 +744,22 @@ cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) ...@@ -734,15 +744,22 @@ cifs_iget(struct super_block *sb, struct cifs_fattr *fattr)
unsigned long hash; unsigned long hash;
struct inode *inode; struct inode *inode;
retry_iget5_locked:
cFYI(1, ("looking for uniqueid=%llu", fattr->cf_uniqueid)); cFYI(1, ("looking for uniqueid=%llu", fattr->cf_uniqueid));
/* hash down to 32-bits on 32-bit arch */ /* hash down to 32-bits on 32-bit arch */
hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid);
inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr); inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr);
/* we have fattrs in hand, update the inode */
if (inode) { if (inode) {
/* was there a problematic inode number collision? */
if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) {
iput(inode);
fattr->cf_uniqueid = iunique(sb, ROOT_I);
fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION;
goto retry_iget5_locked;
}
cifs_fattr_to_inode(inode, fattr); cifs_fattr_to_inode(inode, fattr);
if (sb->s_flags & MS_NOATIME) if (sb->s_flags & MS_NOATIME)
inode->i_flags |= S_NOATIME | S_NOCMTIME; inode->i_flags |= S_NOATIME | S_NOCMTIME;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册