diff options
author | Steven J. Magnani <steve@digidescorp.com> | 2012-10-04 20:14:45 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-05 14:05:09 -0400 |
commit | 7669e8fb09da47dd45c07a51394f01031ea81da8 (patch) | |
tree | 1112667a0606e27999a18b8dae574ae8ad01a151 /fs/fat/nfs.c | |
parent | 21b6633d516c4f5d03ec02ede6374e320191003f (diff) |
fat (exportfs): fix dentry reconnection
Maintain an index of directory inodes by starting cluster, so that
fat_get_parent() can return the proper cached inode rather than inventing
one that cannot be traced back to the filesystem root.
Add a new msdos/vfat binary mount option "nfs" so that FAT filesystems
that are _not_ exported via NFS are not saddled with maintenance of an
index they will never use.
Finally, simplify NFS file handle generation and lookups. An
ext2-congruent implementation is adequate for FAT needs.
Signed-off-by: Steven J. Magnani <steve@digidescorp.com>
Acked-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/nfs.c')
-rw-r--r-- | fs/fat/nfs.c | 150 |
1 files changed, 50 insertions, 100 deletions
diff --git a/fs/fat/nfs.c b/fs/fat/nfs.c index 21609a1e9355..ef4b5faba87b 100644 --- a/fs/fat/nfs.c +++ b/fs/fat/nfs.c | |||
@@ -14,47 +14,46 @@ | |||
14 | #include <linux/exportfs.h> | 14 | #include <linux/exportfs.h> |
15 | #include "fat.h" | 15 | #include "fat.h" |
16 | 16 | ||
17 | /* | 17 | /** |
18 | * a FAT file handle with fhtype 3 is | 18 | * Look up a directory inode given its starting cluster. |
19 | * 0/ i_ino - for fast, reliable lookup if still in the cache | ||
20 | * 1/ i_generation - to see if i_ino is still valid | ||
21 | * bit 0 == 0 iff directory | ||
22 | * 2/ i_pos(8-39) - if ino has changed, but still in cache | ||
23 | * 3/ i_pos(4-7)|i_logstart - to semi-verify inode found at i_pos | ||
24 | * 4/ i_pos(0-3)|parent->i_logstart - maybe used to hunt for the file on disc | ||
25 | * | ||
26 | * Hack for NFSv2: Maximum FAT entry number is 28bits and maximum | ||
27 | * i_pos is 40bits (blocknr(32) + dir offset(8)), so two 4bits | ||
28 | * of i_logstart is used to store the directory entry offset. | ||
29 | */ | 19 | */ |
30 | 20 | static struct inode *fat_dget(struct super_block *sb, int i_logstart) | |
31 | int | ||
32 | fat_encode_fh(struct inode *inode, __u32 *fh, int *lenp, struct inode *parent) | ||
33 | { | 21 | { |
34 | int len = *lenp; | 22 | struct msdos_sb_info *sbi = MSDOS_SB(sb); |
35 | struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); | 23 | struct hlist_head *head; |
36 | loff_t i_pos; | 24 | struct hlist_node *_p; |
25 | struct msdos_inode_info *i; | ||
26 | struct inode *inode = NULL; | ||
37 | 27 | ||
38 | if (len < 5) { | 28 | head = sbi->dir_hashtable + fat_dir_hash(i_logstart); |
39 | *lenp = 5; | 29 | spin_lock(&sbi->dir_hash_lock); |
40 | return 255; /* no room */ | 30 | hlist_for_each_entry(i, _p, head, i_dir_hash) { |
31 | BUG_ON(i->vfs_inode.i_sb != sb); | ||
32 | if (i->i_logstart != i_logstart) | ||
33 | continue; | ||
34 | inode = igrab(&i->vfs_inode); | ||
35 | if (inode) | ||
36 | break; | ||
41 | } | 37 | } |
42 | 38 | spin_unlock(&sbi->dir_hash_lock); | |
43 | i_pos = fat_i_pos_read(sbi, inode); | 39 | return inode; |
44 | *lenp = 5; | ||
45 | fh[0] = inode->i_ino; | ||
46 | fh[1] = inode->i_generation; | ||
47 | fh[2] = i_pos >> 8; | ||
48 | fh[3] = ((i_pos & 0xf0) << 24) | MSDOS_I(inode)->i_logstart; | ||
49 | fh[4] = (i_pos & 0x0f) << 28; | ||
50 | if (parent) | ||
51 | fh[4] |= MSDOS_I(parent)->i_logstart; | ||
52 | return 3; | ||
53 | } | 40 | } |
54 | 41 | ||
55 | static int fat_is_valid_fh(int fh_len, int fh_type) | 42 | static struct inode *fat_nfs_get_inode(struct super_block *sb, |
43 | u64 ino, u32 generation) | ||
56 | { | 44 | { |
57 | return ((fh_len >= 5) && (fh_type == 3)); | 45 | struct inode *inode; |
46 | |||
47 | if ((ino < MSDOS_ROOT_INO) || (ino == MSDOS_FSINFO_INO)) | ||
48 | return NULL; | ||
49 | |||
50 | inode = ilookup(sb, ino); | ||
51 | if (inode && generation && (inode->i_generation != generation)) { | ||
52 | iput(inode); | ||
53 | inode = NULL; | ||
54 | } | ||
55 | |||
56 | return inode; | ||
58 | } | 57 | } |
59 | 58 | ||
60 | /** | 59 | /** |
@@ -64,57 +63,19 @@ static int fat_is_valid_fh(int fh_len, int fh_type) | |||
64 | struct dentry *fat_fh_to_dentry(struct super_block *sb, struct fid *fid, | 63 | struct dentry *fat_fh_to_dentry(struct super_block *sb, struct fid *fid, |
65 | int fh_len, int fh_type) | 64 | int fh_len, int fh_type) |
66 | { | 65 | { |
67 | struct inode *inode = NULL; | 66 | return generic_fh_to_dentry(sb, fid, fh_len, fh_type, |
68 | u32 *fh = fid->raw; | 67 | fat_nfs_get_inode); |
69 | loff_t i_pos; | 68 | } |
70 | unsigned long i_ino; | ||
71 | __u32 i_generation; | ||
72 | int i_logstart; | ||
73 | |||
74 | if (!fat_is_valid_fh(fh_len, fh_type)) | ||
75 | return NULL; | ||
76 | |||
77 | i_ino = fh[0]; | ||
78 | i_generation = fh[1]; | ||
79 | i_logstart = fh[3] & 0x0fffffff; | ||
80 | |||
81 | /* Try i_ino lookup first - fastest and most reliable */ | ||
82 | inode = ilookup(sb, i_ino); | ||
83 | if (inode && (inode->i_generation != i_generation)) { | ||
84 | iput(inode); | ||
85 | inode = NULL; | ||
86 | } | ||
87 | if (!inode) { | ||
88 | i_pos = (loff_t)fh[2] << 8; | ||
89 | i_pos |= ((fh[3] >> 24) & 0xf0) | (fh[4] >> 28); | ||
90 | |||
91 | /* try 2 - see if i_pos is in F-d-c | ||
92 | * require i_logstart to be the same | ||
93 | * Will fail if you truncate and then re-write | ||
94 | */ | ||
95 | |||
96 | inode = fat_iget(sb, i_pos); | ||
97 | if (inode && MSDOS_I(inode)->i_logstart != i_logstart) { | ||
98 | iput(inode); | ||
99 | inode = NULL; | ||
100 | } | ||
101 | } | ||
102 | 69 | ||
103 | /* | 70 | /* |
104 | * For now, do nothing if the inode is not found. | 71 | * Find the parent for a file specified by NFS handle. |
105 | * | 72 | * This requires that the handle contain the i_ino of the parent. |
106 | * What we could do is: | 73 | */ |
107 | * | 74 | struct dentry *fat_fh_to_parent(struct super_block *sb, struct fid *fid, |
108 | * - follow the file starting at fh[4], and record the ".." entry, | 75 | int fh_len, int fh_type) |
109 | * and the name of the fh[2] entry. | 76 | { |
110 | * - then follow the ".." file finding the next step up. | 77 | return generic_fh_to_parent(sb, fid, fh_len, fh_type, |
111 | * | 78 | fat_nfs_get_inode); |
112 | * This way we build a path to the root of the tree. If this works, we | ||
113 | * lookup the path and so get this inode into the cache. Finally try | ||
114 | * the fat_iget lookup again. If that fails, then we are totally out | ||
115 | * of luck. But all that is for another day | ||
116 | */ | ||
117 | return d_obtain_alias(inode); | ||
118 | } | 79 | } |
119 | 80 | ||
120 | /* | 81 | /* |
@@ -128,24 +89,13 @@ struct dentry *fat_get_parent(struct dentry *child_dir) | |||
128 | struct super_block *sb = child_dir->d_sb; | 89 | struct super_block *sb = child_dir->d_sb; |
129 | struct buffer_head *bh = NULL; | 90 | struct buffer_head *bh = NULL; |
130 | struct msdos_dir_entry *de; | 91 | struct msdos_dir_entry *de; |
131 | loff_t i_pos; | 92 | struct inode *parent_inode = NULL; |
132 | struct dentry *parent; | ||
133 | struct inode *inode; | ||
134 | int err; | ||
135 | 93 | ||
136 | lock_super(sb); | 94 | if (!fat_get_dotdot_entry(child_dir->d_inode, &bh, &de)) { |
137 | 95 | int parent_logstart = fat_get_start(MSDOS_SB(sb), de); | |
138 | err = fat_get_dotdot_entry(child_dir->d_inode, &bh, &de, &i_pos); | 96 | parent_inode = fat_dget(sb, parent_logstart); |
139 | if (err) { | ||
140 | parent = ERR_PTR(err); | ||
141 | goto out; | ||
142 | } | 97 | } |
143 | inode = fat_build_inode(sb, de, i_pos); | ||
144 | |||
145 | parent = d_obtain_alias(inode); | ||
146 | out: | ||
147 | brelse(bh); | 98 | brelse(bh); |
148 | unlock_super(sb); | ||
149 | 99 | ||
150 | return parent; | 100 | return d_obtain_alias(parent_inode); |
151 | } | 101 | } |