Re: [PATCH] cifs: guard against hardlinking directories
Steve French <[email protected]> Fri, 7 May 2010 09:34:24 -0500
| Newsgroups | gmane.linux.file-systems.cifs |
|---|---|
| Message-ID | <[email protected]> |
I have reviewed this and this looks good - just need a tested by or reviewed by. On Fri, May 7, 2010 at 9:03 AM, Jeff Layton <[email protected]> wrote: > 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. > > Signed-off-by: Jeff Layton <[email protected]> > --- > fs/cifs/cifsglob.h | 1 + > fs/cifs/inode.c | 21 +++++++++++++++++++-- > 2 files changed, 20 insertions(+), 2 deletions(-) > > diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h > index ecf0ffb..0c2fd17 100644 > --- a/fs/cifs/cifsglob.h > +++ b/fs/cifs/cifsglob.h > @@ -502,6 +502,7 @@ struct dfs_info3_param { > #define CIFS_FATTR_DFS_REFERRAL 0x1 > #define CIFS_FATTR_DELETE_PENDING 0x2 > #define CIFS_FATTR_NEED_REVAL 0x4 > +#define CIFS_FATTR_INO_COLLISION 0x8 > > struct cifs_fattr { > u32 cf_flags; > diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c > index ee35c97..fb20f11 100644 > --- a/fs/cifs/inode.c > +++ b/fs/cifs/inode.c > @@ -714,6 +714,16 @@ cifs_find_inode(struct inode *inode, void *opaque) > if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid) > 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)) { > + fattr->cf_flags |= CIFS_FATTR_INO_COLLISION; > + cifs_autodisable_serverino(CIFS_SB(inode->i_sb)); > + } > + > return 1; > } > > @@ -733,15 +743,22 @@ cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) > unsigned long hash; > struct inode *inode; > > +retry_iget5_locked: > cFYI(1, ("looking for uniqueid=%llu", fattr->cf_uniqueid)); > > /* hash down to 32-bits on 32-bit arch */ > hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); > > inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr); > - > - /* we have fattrs in hand, update the 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); > if (sb->s_flags & MS_NOATIME) > inode->i_flags |= S_NOATIME | S_NOCMTIME; > -- > 1.7.0.1 > > -- Thanks, Steve