aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2010-05-11 14:59:55 -0400
committerSteve French <sfrench@us.ibm.com>2010-05-11 16:57:50 -0400
commit3d69438031b00c601c991ab447cafb7d5c3c59a6 (patch)
treef40d66a255c4169b43079252a4c96868c097ddab
parent05ce7bfe547c9fa967d9cab6c37867a9cb6fb3fa (diff)
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: Jeff Layton <jlayton@redhat.com> CC: Stable <stable@kernel.org> Reviewed-and-Tested-by: Suresh Jayaraman <sjayaraman@suse.de> Signed-off-by: Steve French <sfrench@us.ibm.com>
-rw-r--r--fs/cifs/cifsglob.h1
-rw-r--r--fs/cifs/inode.c21
2 files changed, 20 insertions, 2 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index ecf0ffbe2b64..0c2fd17439c8 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -502,6 +502,7 @@ struct dfs_info3_param {
502#define CIFS_FATTR_DFS_REFERRAL 0x1 502#define CIFS_FATTR_DFS_REFERRAL 0x1
503#define CIFS_FATTR_DELETE_PENDING 0x2 503#define CIFS_FATTR_DELETE_PENDING 0x2
504#define CIFS_FATTR_NEED_REVAL 0x4 504#define CIFS_FATTR_NEED_REVAL 0x4
505#define CIFS_FATTR_INO_COLLISION 0x8
505 506
506struct cifs_fattr { 507struct cifs_fattr {
507 u32 cf_flags; 508 u32 cf_flags;
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 35ec11716213..29b9ea244c81 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -715,6 +715,16 @@ cifs_find_inode(struct inode *inode, void *opaque)
715 if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid) 715 if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid)
716 return 0; 716 return 0;
717 717
718 /*
719 * uh oh -- it's a directory. We can't use it since hardlinked dirs are
720 * verboten. Disable serverino and return it as if it were found, the
721 * caller can discard it, generate a uniqueid and retry the find
722 */
723 if (S_ISDIR(inode->i_mode) && !list_empty(&inode->i_dentry)) {
724 fattr->cf_flags |= CIFS_FATTR_INO_COLLISION;
725 cifs_autodisable_serverino(CIFS_SB(inode->i_sb));
726 }
727
718 return 1; 728 return 1;
719} 729}
720 730
@@ -734,15 +744,22 @@ cifs_iget(struct super_block *sb, struct cifs_fattr *fattr)
734 unsigned long hash; 744 unsigned long hash;
735 struct inode *inode; 745 struct inode *inode;
736 746
747retry_iget5_locked:
737 cFYI(1, ("looking for uniqueid=%llu", fattr->cf_uniqueid)); 748 cFYI(1, ("looking for uniqueid=%llu", fattr->cf_uniqueid));
738 749
739 /* hash down to 32-bits on 32-bit arch */ 750 /* hash down to 32-bits on 32-bit arch */
740 hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); 751 hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid);
741 752
742 inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr); 753 inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr);
743
744 /* we have fattrs in hand, update the inode */
745 if (inode) { 754 if (inode) {
755 /* was there a problematic inode number collision? */
756 if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) {
757 iput(inode);
758 fattr->cf_uniqueid = iunique(sb, ROOT_I);
759 fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION;
760 goto retry_iget5_locked;
761 }
762
746 cifs_fattr_to_inode(inode, fattr); 763 cifs_fattr_to_inode(inode, fattr);
747 if (sb->s_flags & MS_NOATIME) 764 if (sb->s_flags & MS_NOATIME)
748 inode->i_flags |= S_NOATIME | S_NOCMTIME; 765 inode->i_flags |= S_NOATIME | S_NOCMTIME;