fat: Fix vfat_lookup()
authorOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Mon, 11 Jan 2010 18:32:24 +0000 (03:32 +0900)
committerOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Mon, 11 Jan 2010 18:47:25 +0000 (03:47 +0900)
After d_find_alias(), vfat_lookup() checks !(->d_flags & DCACHE_DISCONNECTED)
without IS_ROOT().  This means it hits non-anonymous but disconnected
dentry. (NOTE: d_splice_alias() doesn't clear DCACHE_DISCONNECTED)

But, vfat_lookup() has interest to alias if it was non-anonymous. So,
this adds vfat_d_anon_disconn() helper to check it correctly.

Another bug is refcnt leak. It needs dput() for uninterested alias.

Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
fs/fat/namei_vfat.c

index f565f24..411c192 100644 (file)
@@ -701,6 +701,15 @@ static int vfat_find(struct inode *dir, struct qstr *qname,
        return fat_search_long(dir, qname->name, len, sinfo);
 }
 
        return fat_search_long(dir, qname->name, len, sinfo);
 }
 
+/*
+ * (nfsd's) anonymous disconnected dentry?
+ * NOTE: !IS_ROOT() is not anonymous (I.e. d_splice_alias() did the job).
+ */
+static int vfat_d_anon_disconn(struct dentry *dentry)
+{
+       return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED);
+}
+
 static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
                                  struct nameidata *nd)
 {
 static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
                                  struct nameidata *nd)
 {
@@ -729,11 +738,11 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
        }
 
        alias = d_find_alias(inode);
        }
 
        alias = d_find_alias(inode);
-       if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) {
+       if (alias && !vfat_d_anon_disconn(alias)) {
                /*
                /*
-                * This inode has non DCACHE_DISCONNECTED dentry. This
-                * means, the user did ->lookup() by an another name
-                * (longname vs 8.3 alias of it) in past.
+                * This inode has non anonymous-DCACHE_DISCONNECTED
+                * dentry. This means, the user did ->lookup() by an
+                * another name (longname vs 8.3 alias of it) in past.
                 *
                 * Switch to new one for reason of locality if possible.
                 */
                 *
                 * Switch to new one for reason of locality if possible.
                 */
@@ -743,7 +752,9 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
                iput(inode);
                unlock_super(sb);
                return alias;
                iput(inode);
                unlock_super(sb);
                return alias;
-       }
+       } else
+               dput(alias);
+
 out:
        unlock_super(sb);
        dentry->d_op = sb->s_root->d_op;
 out:
        unlock_super(sb);
        dentry->d_op = sb->s_root->d_op;