diff options
author | OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> | 2008-11-06 15:53:51 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-11-06 18:41:21 -0500 |
commit | 1b52467243c7167b3a267ddbcbb14d550f28eb4a (patch) | |
tree | f55bae4d0b970216bda77c29094f19575f3b6c42 /fs/fat | |
parent | 068f5ae05c51d2cee6b31cb3da06775dd83bd348 (diff) |
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>
Diffstat (limited to 'fs/fat')
-rw-r--r-- | fs/fat/namei_vfat.c | 124 |
1 files changed, 80 insertions, 44 deletions
diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 419deabfb9be..d585398f9f6b 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c | |||
@@ -24,27 +24,67 @@ | |||
24 | #include <linux/namei.h> | 24 | #include <linux/namei.h> |
25 | #include "fat.h" | 25 | #include "fat.h" |
26 | 26 | ||
27 | static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd) | 27 | /* |
28 | * If new entry was created in the parent, it could create the 8.3 | ||
29 | * alias (the shortname of logname). So, the parent may have the | ||
30 | * negative-dentry which matches the created 8.3 alias. | ||
31 | * | ||
32 | * If it happened, the negative dentry isn't actually negative | ||
33 | * anymore. So, drop it. | ||
34 | */ | ||
35 | static int vfat_revalidate_shortname(struct dentry *dentry) | ||
28 | { | 36 | { |
29 | int ret = 1; | 37 | int ret = 1; |
30 | 38 | spin_lock(&dentry->d_lock); | |
31 | if (!dentry->d_inode && | 39 | if (dentry->d_time != dentry->d_parent->d_inode->i_version) |
32 | nd && !(nd->flags & LOOKUP_CONTINUE) && (nd->flags & LOOKUP_CREATE)) | ||
33 | /* | ||
34 | * negative dentry is dropped, in order to make sure | ||
35 | * to use the name which a user desires if this is | ||
36 | * create path. | ||
37 | */ | ||
38 | ret = 0; | 40 | ret = 0; |
39 | else { | 41 | spin_unlock(&dentry->d_lock); |
40 | spin_lock(&dentry->d_lock); | ||
41 | if (dentry->d_time != dentry->d_parent->d_inode->i_version) | ||
42 | ret = 0; | ||
43 | spin_unlock(&dentry->d_lock); | ||
44 | } | ||
45 | return ret; | 42 | return ret; |
46 | } | 43 | } |
47 | 44 | ||
45 | static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd) | ||
46 | { | ||
47 | /* This is not negative dentry. Always valid. */ | ||
48 | if (dentry->d_inode) | ||
49 | return 1; | ||
50 | return vfat_revalidate_shortname(dentry); | ||
51 | } | ||
52 | |||
53 | static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd) | ||
54 | { | ||
55 | /* | ||
56 | * This is not negative dentry. Always valid. | ||
57 | * | ||
58 | * Note, rename() to existing directory entry will have ->d_inode, | ||
59 | * and will use existing name which isn't specified name by user. | ||
60 | * | ||
61 | * We may be able to drop this positive dentry here. But dropping | ||
62 | * positive dentry isn't good idea. So it's unsupported like | ||
63 | * rename("filename", "FILENAME") for now. | ||
64 | */ | ||
65 | if (dentry->d_inode) | ||
66 | return 1; | ||
67 | |||
68 | /* | ||
69 | * This may be nfsd (or something), anyway, we can't see the | ||
70 | * intent of this. So, since this can be for creation, drop it. | ||
71 | */ | ||
72 | if (!nd) | ||
73 | return 0; | ||
74 | |||
75 | /* | ||
76 | * Drop the negative dentry, in order to make sure to use the | ||
77 | * case sensitive name which is specified by user if this is | ||
78 | * for creation. | ||
79 | */ | ||
80 | if (!(nd->flags & (LOOKUP_CONTINUE | LOOKUP_PARENT))) { | ||
81 | if (nd->flags & LOOKUP_CREATE) | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | return vfat_revalidate_shortname(dentry); | ||
86 | } | ||
87 | |||
48 | /* returns the length of a struct qstr, ignoring trailing dots */ | 88 | /* returns the length of a struct qstr, ignoring trailing dots */ |
49 | static unsigned int vfat_striptail_len(struct qstr *qstr) | 89 | static unsigned int vfat_striptail_len(struct qstr *qstr) |
50 | { | 90 | { |
@@ -126,25 +166,16 @@ static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b) | |||
126 | return 1; | 166 | return 1; |
127 | } | 167 | } |
128 | 168 | ||
129 | static struct dentry_operations vfat_dentry_ops[4] = { | 169 | static struct dentry_operations vfat_ci_dentry_ops = { |
130 | { | 170 | .d_revalidate = vfat_revalidate_ci, |
131 | .d_hash = vfat_hashi, | 171 | .d_hash = vfat_hashi, |
132 | .d_compare = vfat_cmpi, | 172 | .d_compare = vfat_cmpi, |
133 | }, | 173 | }; |
134 | { | 174 | |
135 | .d_revalidate = vfat_revalidate, | 175 | static struct dentry_operations vfat_dentry_ops = { |
136 | .d_hash = vfat_hashi, | 176 | .d_revalidate = vfat_revalidate, |
137 | .d_compare = vfat_cmpi, | 177 | .d_hash = vfat_hash, |
138 | }, | 178 | .d_compare = vfat_cmp, |
139 | { | ||
140 | .d_hash = vfat_hash, | ||
141 | .d_compare = vfat_cmp, | ||
142 | }, | ||
143 | { | ||
144 | .d_revalidate = vfat_revalidate, | ||
145 | .d_hash = vfat_hash, | ||
146 | .d_compare = vfat_cmp, | ||
147 | } | ||
148 | }; | 179 | }; |
149 | 180 | ||
150 | /* Characters that are undesirable in an MS-DOS file name */ | 181 | /* 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, | |||
685 | struct fat_slot_info sinfo; | 716 | struct fat_slot_info sinfo; |
686 | struct inode *inode; | 717 | struct inode *inode; |
687 | struct dentry *alias; | 718 | struct dentry *alias; |
688 | int err, table; | 719 | int err; |
689 | 720 | ||
690 | lock_super(sb); | 721 | lock_super(sb); |
691 | table = (MSDOS_SB(sb)->options.name_check == 's') ? 2 : 0; | ||
692 | dentry->d_op = &vfat_dentry_ops[table]; | ||
693 | 722 | ||
694 | err = vfat_find(dir, &dentry->d_name, &sinfo); | 723 | err = vfat_find(dir, &dentry->d_name, &sinfo); |
695 | if (err) { | 724 | if (err) { |
696 | if (err == -ENOENT) { | 725 | if (err == -ENOENT) { |
697 | table++; | ||
698 | inode = NULL; | 726 | inode = NULL; |
699 | goto out; | 727 | goto out; |
700 | } | 728 | } |
701 | goto error; | 729 | goto error; |
702 | } | 730 | } |
731 | |||
703 | inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); | 732 | inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); |
704 | brelse(sinfo.bh); | 733 | brelse(sinfo.bh); |
705 | if (IS_ERR(inode)) { | 734 | if (IS_ERR(inode)) { |
706 | err = PTR_ERR(inode); | 735 | err = PTR_ERR(inode); |
707 | goto error; | 736 | goto error; |
708 | } | 737 | } |
738 | |||
709 | alias = d_find_alias(inode); | 739 | alias = d_find_alias(inode); |
710 | if (alias) { | 740 | if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) { |
741 | /* | ||
742 | * This inode has non DCACHE_DISCONNECTED dentry. This | ||
743 | * means, the user did ->lookup() by an another name | ||
744 | * (longname vs 8.3 alias of it) in past. | ||
745 | * | ||
746 | * Switch to new one for reason of locality if possible. | ||
747 | */ | ||
711 | if (d_invalidate(alias) == 0) | 748 | if (d_invalidate(alias) == 0) |
712 | dput(alias); | 749 | dput(alias); |
713 | else { | 750 | else { |
@@ -715,15 +752,14 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry, | |||
715 | unlock_super(sb); | 752 | unlock_super(sb); |
716 | return alias; | 753 | return alias; |
717 | } | 754 | } |
718 | |||
719 | } | 755 | } |
720 | out: | 756 | out: |
721 | unlock_super(sb); | 757 | unlock_super(sb); |
722 | dentry->d_op = &vfat_dentry_ops[table]; | 758 | dentry->d_op = sb->s_root->d_op; |
723 | dentry->d_time = dentry->d_parent->d_inode->i_version; | 759 | dentry->d_time = dentry->d_parent->d_inode->i_version; |
724 | dentry = d_splice_alias(inode, dentry); | 760 | dentry = d_splice_alias(inode, dentry); |
725 | if (dentry) { | 761 | if (dentry) { |
726 | dentry->d_op = &vfat_dentry_ops[table]; | 762 | dentry->d_op = sb->s_root->d_op; |
727 | dentry->d_time = dentry->d_parent->d_inode->i_version; | 763 | dentry->d_time = dentry->d_parent->d_inode->i_version; |
728 | } | 764 | } |
729 | return dentry; | 765 | return dentry; |
@@ -1022,9 +1058,9 @@ static int vfat_fill_super(struct super_block *sb, void *data, int silent) | |||
1022 | return res; | 1058 | return res; |
1023 | 1059 | ||
1024 | if (MSDOS_SB(sb)->options.name_check != 's') | 1060 | if (MSDOS_SB(sb)->options.name_check != 's') |
1025 | sb->s_root->d_op = &vfat_dentry_ops[0]; | 1061 | sb->s_root->d_op = &vfat_ci_dentry_ops; |
1026 | else | 1062 | else |
1027 | sb->s_root->d_op = &vfat_dentry_ops[2]; | 1063 | sb->s_root->d_op = &vfat_dentry_ops; |
1028 | 1064 | ||
1029 | return 0; | 1065 | return 0; |
1030 | } | 1066 | } |