fat: Fix/Cleanup dcache handling for vfat
- Add comments for handling dcache of vfat. - Separate case-sensitive case and case-insensitive to vfat_revalidate() and vfat_ci_revalidate(). vfat_revalidate() doesn't need to drop case-insensitive negative dentry on creation path. - Current code is missing to set ->d_revalidate to the negative dentry created by unlink/etc.. This sets ->d_revalidate always, and returns 1 for positive dentry. Now, we don't need to change ->d_op dynamically anymore, so this just uses sb->s_root->d_op to set ->d_op. - d_find_alias() may return DCACHE_DISCONNECTED dentry. It's not the interesting dentry there. This checks it. - Add missing LOOKUP_PARENT check. We don't need to drop the valid negative dentry for (LOOKUP_CREATE | LOOKUP_PARENT) lookup. - For consistent filename on creation path, this drops negative dentry if we can't see intent. Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
068f5ae05c
commit
1b52467243
|
@ -24,27 +24,67 @@
|
|||
#include <linux/namei.h>
|
||||
#include "fat.h"
|
||||
|
||||
static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
/*
|
||||
* If new entry was created in the parent, it could create the 8.3
|
||||
* alias (the shortname of logname). So, the parent may have the
|
||||
* negative-dentry which matches the created 8.3 alias.
|
||||
*
|
||||
* If it happened, the negative dentry isn't actually negative
|
||||
* anymore. So, drop it.
|
||||
*/
|
||||
static int vfat_revalidate_shortname(struct dentry *dentry)
|
||||
{
|
||||
int ret = 1;
|
||||
|
||||
if (!dentry->d_inode &&
|
||||
nd && !(nd->flags & LOOKUP_CONTINUE) && (nd->flags & LOOKUP_CREATE))
|
||||
/*
|
||||
* negative dentry is dropped, in order to make sure
|
||||
* to use the name which a user desires if this is
|
||||
* create path.
|
||||
*/
|
||||
ret = 0;
|
||||
else {
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (dentry->d_time != dentry->d_parent->d_inode->i_version)
|
||||
ret = 0;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
/* This is not negative dentry. Always valid. */
|
||||
if (dentry->d_inode)
|
||||
return 1;
|
||||
return vfat_revalidate_shortname(dentry);
|
||||
}
|
||||
|
||||
static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
/*
|
||||
* This is not negative dentry. Always valid.
|
||||
*
|
||||
* Note, rename() to existing directory entry will have ->d_inode,
|
||||
* and will use existing name which isn't specified name by user.
|
||||
*
|
||||
* We may be able to drop this positive dentry here. But dropping
|
||||
* positive dentry isn't good idea. So it's unsupported like
|
||||
* rename("filename", "FILENAME") for now.
|
||||
*/
|
||||
if (dentry->d_inode)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* This may be nfsd (or something), anyway, we can't see the
|
||||
* intent of this. So, since this can be for creation, drop it.
|
||||
*/
|
||||
if (!nd)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Drop the negative dentry, in order to make sure to use the
|
||||
* case sensitive name which is specified by user if this is
|
||||
* for creation.
|
||||
*/
|
||||
if (!(nd->flags & (LOOKUP_CONTINUE | LOOKUP_PARENT))) {
|
||||
if (nd->flags & LOOKUP_CREATE)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return vfat_revalidate_shortname(dentry);
|
||||
}
|
||||
|
||||
/* returns the length of a struct qstr, ignoring trailing dots */
|
||||
static unsigned int vfat_striptail_len(struct qstr *qstr)
|
||||
{
|
||||
|
@ -126,25 +166,16 @@ static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static struct dentry_operations vfat_dentry_ops[4] = {
|
||||
{
|
||||
static struct dentry_operations vfat_ci_dentry_ops = {
|
||||
.d_revalidate = vfat_revalidate_ci,
|
||||
.d_hash = vfat_hashi,
|
||||
.d_compare = vfat_cmpi,
|
||||
},
|
||||
{
|
||||
.d_revalidate = vfat_revalidate,
|
||||
.d_hash = vfat_hashi,
|
||||
.d_compare = vfat_cmpi,
|
||||
},
|
||||
{
|
||||
.d_hash = vfat_hash,
|
||||
.d_compare = vfat_cmp,
|
||||
},
|
||||
{
|
||||
};
|
||||
|
||||
static struct dentry_operations vfat_dentry_ops = {
|
||||
.d_revalidate = vfat_revalidate,
|
||||
.d_hash = vfat_hash,
|
||||
.d_compare = vfat_cmp,
|
||||
}
|
||||
};
|
||||
|
||||
/* Characters that are undesirable in an MS-DOS file name */
|
||||
|
@ -685,29 +716,35 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
|
|||
struct fat_slot_info sinfo;
|
||||
struct inode *inode;
|
||||
struct dentry *alias;
|
||||
int err, table;
|
||||
int err;
|
||||
|
||||
lock_super(sb);
|
||||
table = (MSDOS_SB(sb)->options.name_check == 's') ? 2 : 0;
|
||||
dentry->d_op = &vfat_dentry_ops[table];
|
||||
|
||||
err = vfat_find(dir, &dentry->d_name, &sinfo);
|
||||
if (err) {
|
||||
if (err == -ENOENT) {
|
||||
table++;
|
||||
inode = NULL;
|
||||
goto out;
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
|
||||
inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
|
||||
brelse(sinfo.bh);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
goto error;
|
||||
}
|
||||
|
||||
alias = d_find_alias(inode);
|
||||
if (alias) {
|
||||
if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) {
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Switch to new one for reason of locality if possible.
|
||||
*/
|
||||
if (d_invalidate(alias) == 0)
|
||||
dput(alias);
|
||||
else {
|
||||
|
@ -715,15 +752,14 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
|
|||
unlock_super(sb);
|
||||
return alias;
|
||||
}
|
||||
|
||||
}
|
||||
out:
|
||||
unlock_super(sb);
|
||||
dentry->d_op = &vfat_dentry_ops[table];
|
||||
dentry->d_op = sb->s_root->d_op;
|
||||
dentry->d_time = dentry->d_parent->d_inode->i_version;
|
||||
dentry = d_splice_alias(inode, dentry);
|
||||
if (dentry) {
|
||||
dentry->d_op = &vfat_dentry_ops[table];
|
||||
dentry->d_op = sb->s_root->d_op;
|
||||
dentry->d_time = dentry->d_parent->d_inode->i_version;
|
||||
}
|
||||
return dentry;
|
||||
|
@ -1022,9 +1058,9 @@ static int vfat_fill_super(struct super_block *sb, void *data, int silent)
|
|||
return res;
|
||||
|
||||
if (MSDOS_SB(sb)->options.name_check != 's')
|
||||
sb->s_root->d_op = &vfat_dentry_ops[0];
|
||||
sb->s_root->d_op = &vfat_ci_dentry_ops;
|
||||
else
|
||||
sb->s_root->d_op = &vfat_dentry_ops[2];
|
||||
sb->s_root->d_op = &vfat_dentry_ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue