]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - fs/fat/nfs.c
fat: (exportfs) move code to rebuild directory-inode to separate function
[karo-tx-linux.git] / fs / fat / nfs.c
1 /* fs/fat/nfs.c
2  *
3  * This software is licensed under the terms of the GNU General Public
4  * License version 2, as published by the Free Software Foundation, and
5  * may be copied, distributed, and modified under those terms.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  */
13
14 #include <linux/exportfs.h>
15 #include "fat.h"
16
17 struct fat_fid {
18         u32 i_gen;
19         u32 i_pos_low;
20         u16 i_pos_hi;
21         u16 parent_i_pos_hi;
22         u32 parent_i_pos_low;
23         u32 parent_i_gen;
24 } __packed;
25
26 #define FILEID_FAT_WITHOUT_PARENT (offsetof(struct fat_fid, parent_i_pos_hi)/4)
27 #define FILEID_FAT_WITH_PARENT (sizeof(struct fat_fid)/4)
28
29 /**
30  * Look up a directory inode given its starting cluster.
31  */
32 static struct inode *fat_dget(struct super_block *sb, int i_logstart)
33 {
34         struct msdos_sb_info *sbi = MSDOS_SB(sb);
35         struct hlist_head *head;
36         struct hlist_node *_p;
37         struct msdos_inode_info *i;
38         struct inode *inode = NULL;
39
40         head = sbi->dir_hashtable + fat_dir_hash(i_logstart);
41         spin_lock(&sbi->dir_hash_lock);
42         hlist_for_each_entry(i, _p, head, i_dir_hash) {
43                 BUG_ON(i->vfs_inode.i_sb != sb);
44                 if (i->i_logstart != i_logstart)
45                         continue;
46                 inode = igrab(&i->vfs_inode);
47                 if (inode)
48                         break;
49         }
50         spin_unlock(&sbi->dir_hash_lock);
51         return inode;
52 }
53
54 static struct inode *fat_ilookup(struct super_block *sb, u64 ino, loff_t i_pos)
55 {
56         if (MSDOS_SB(sb)->options.nfs == FAT_NFS_NOSTALE_RO)
57                 return fat_iget(sb, i_pos);
58
59         else {
60                 if ((ino < MSDOS_ROOT_INO) || (ino == MSDOS_FSINFO_INO))
61                         return NULL;
62                 return ilookup(sb, ino);
63         }
64 }
65
66 static struct inode *fat_nfs_get_inode(struct super_block *sb,
67                                        u64 ino, u32 generation, loff_t i_pos)
68 {
69
70         struct inode *inode = fat_ilookup(sb, ino, i_pos);
71
72         if (inode && generation && (inode->i_generation != generation)) {
73                 iput(inode);
74                 inode = NULL;
75         }
76         if (inode == NULL && MSDOS_SB(sb)->options.nfs == FAT_NFS_NOSTALE_RO) {
77                 struct buffer_head *bh = NULL;
78                 struct msdos_dir_entry *de ;
79                 sector_t blocknr;
80                 int offset;
81                 fat_get_blknr_offset(MSDOS_SB(sb), i_pos, &blocknr, &offset);
82                 bh = sb_bread(sb, blocknr);
83                 if (!bh) {
84                         fat_msg(sb, KERN_ERR,
85                                 "unable to read block(%llu) for building NFS inode",
86                                 (llu)blocknr);
87                         return inode;
88                 }
89                 de = (struct msdos_dir_entry *)bh->b_data;
90                 /* If a file is deleted on server and client is not updated
91                  * yet, we must not build the inode upon a lookup call.
92                  */
93                 if (IS_FREE(de[offset].name))
94                         inode = NULL;
95                 else
96                         inode = fat_build_inode(sb, &de[offset], i_pos);
97                 brelse(bh);
98         }
99
100         return inode;
101 }
102
103
104 int
105 fat_encode_fh_nostale(struct inode *inode, __u32 *fh, int *lenp,
106                                         struct inode *parent)
107 {
108         int len = *lenp;
109         struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
110         struct fat_fid *fid = (struct fat_fid *) fh;
111         loff_t i_pos;
112         int type = FILEID_INO32_GEN;
113
114         if (parent && (len < FILEID_FAT_WITH_PARENT)) {
115                 *lenp = FILEID_FAT_WITH_PARENT;
116                 return 255;
117         } else if (len < FILEID_FAT_WITHOUT_PARENT) {
118                 *lenp = FILEID_FAT_WITHOUT_PARENT;
119                 return 255;
120         }
121
122         i_pos = fat_i_pos_read(sbi, inode);
123         *lenp = FILEID_FAT_WITHOUT_PARENT;
124         fid->i_gen = inode->i_generation;
125         fid->i_pos_low = i_pos & 0xFFFFFFFF;
126         fid->i_pos_hi = (i_pos >> 32) & 0xFF;
127         if (parent) {
128                 i_pos = fat_i_pos_read(sbi, parent);
129                 fid->parent_i_pos_hi = (i_pos >> 32) & 0xFF;
130                 fid->parent_i_pos_low = i_pos & 0xFFFFFFFF;
131                 fid->parent_i_gen = parent->i_generation;
132                 type = FILEID_INO32_GEN_PARENT;
133                 *lenp = FILEID_FAT_WITH_PARENT;
134         }
135
136         return type;
137 }
138
139 /**
140  * Map a NFS file handle to a corresponding dentry.
141  * The dentry may or may not be connected to the filesystem root.
142  */
143 struct dentry *fat_fh_to_dentry(struct super_block *sb, struct fid *fh,
144                                 int fh_len, int fh_type)
145 {
146         struct inode *inode = NULL;
147         if (fh_len < 2)
148                 return NULL;
149
150         switch (fh_type) {
151         case FILEID_INO32_GEN:
152         case FILEID_INO32_GEN_PARENT:
153                 inode = fat_nfs_get_inode(sb, fh->i32.ino, fh->i32.gen, 0);
154                 break;
155         }
156
157         return d_obtain_alias(inode);
158 }
159 struct dentry *fat_fh_to_dentry_nostale(struct super_block *sb, struct fid *fh,
160                                 int fh_len, int fh_type)
161 {
162         struct inode *inode = NULL;
163         struct fat_fid *fid = (struct fat_fid *)fh;
164         loff_t i_pos;
165
166         switch (fh_type) {
167         case FILEID_INO32_GEN:
168                 if (fh_len < FILEID_FAT_WITHOUT_PARENT)
169                         return NULL;
170         case FILEID_INO32_GEN_PARENT:
171                 if ((fh_len < FILEID_FAT_WITH_PARENT) &&
172                         (fh_type == FILEID_INO32_GEN_PARENT))
173                         return NULL;
174                 i_pos = fid->i_pos_hi;
175                 i_pos = (i_pos << 32) | (fid->i_pos_low);
176                 inode = fat_nfs_get_inode(sb, 0, fid->i_gen, i_pos);
177
178                 break;
179         }
180
181         return d_obtain_alias(inode);
182 }
183
184 /*
185  * Find the parent for a file specified by NFS handle.
186  * This requires that the handle contain the i_ino of the parent.
187  */
188 struct dentry *fat_fh_to_parent(struct super_block *sb, struct fid *fid,
189                                 int fh_len, int fh_type)
190 {
191         struct inode *inode = NULL;
192
193         if (fh_len < 2)
194                 return NULL;
195
196         switch (fh_type) {
197         case FILEID_INO32_GEN:
198         case FILEID_INO32_GEN_PARENT:
199                 inode = fat_nfs_get_inode(sb, fid->i32.ino, fid->i32.gen, 0);
200                 break;
201         }
202
203         return d_obtain_alias(inode);
204 }
205
206 struct dentry *fat_fh_to_parent_nostale(struct super_block *sb, struct fid *fh,
207                                 int fh_len, int fh_type)
208 {
209         struct inode *inode = NULL;
210         struct fat_fid *fid = (struct fat_fid *)fh;
211         loff_t i_pos;
212
213         if (fh_len < FILEID_FAT_WITH_PARENT)
214                 return NULL;
215
216         switch (fh_type) {
217         case FILEID_INO32_GEN_PARENT:
218                 i_pos = fid->parent_i_pos_hi;
219                 i_pos = (i_pos << 32) | (fid->parent_i_pos_low);
220                 inode = fat_nfs_get_inode(sb, 0, fid->parent_i_gen, i_pos);
221                 break;
222         }
223
224         return d_obtain_alias(inode);
225 }
226
227 /*
228  * Read the directory entries of 'search_clus' and find the entry
229  * which contains 'match_ipos' for the starting cluster.If the entry
230  * is found, rebuild its inode.
231  */
232 static struct inode *fat_traverse_cluster(struct super_block *sb,
233                                 int search_clus, int match_ipos)
234 {
235         struct msdos_sb_info *sbi = MSDOS_SB(sb);
236         struct buffer_head *bh;
237         sector_t blknr;
238         int parent_ipos, search_ipos;
239         int i;
240         struct msdos_dir_entry *de;
241         struct inode *inode = NULL;
242         int iterations = sbi->cluster_size >> sb->s_blocksize_bits;
243         blknr = fat_clus_to_blknr(sbi, search_clus);
244
245         do {
246                 bh = sb_bread(sb, blknr);
247                 if (!bh) {
248                         fat_msg(sb, KERN_ERR,
249                                 "NFS:unable to read block(%llu) while traversing cluster(%d)",
250                                 (llu)blknr, search_clus);
251                         inode = ERR_PTR(-EIO);
252                         goto out;
253                 }
254                 de = (struct msdos_dir_entry *)bh->b_data;
255                 for (i = 0; i < sbi->dir_per_block; i++) {
256                         if (de[i].name[0] == FAT_ENT_FREE) {
257                                 /*Reached end of directory*/
258                                 brelse(bh);
259                                 inode = ERR_PTR(-ENODATA);
260                                 goto out;
261                         }
262                         if (de[i].name[0] == DELETED_FLAG)
263                                 continue;
264                         if (de[i].attr == ATTR_EXT)
265                                 continue;
266                         if (!(de[i].attr & ATTR_DIR))
267                                 continue;
268                         else {
269                                 search_ipos = fat_get_start(sbi, &de[i]);
270                                 if (search_ipos == match_ipos) {
271                                         /*Success.Now build the inode*/
272                                         parent_ipos = (loff_t)i +
273                                          (blknr << sbi->dir_per_block_bits);
274                                         inode = fat_build_inode(sb, &de[i],
275                                                                 parent_ipos);
276                                         brelse(bh);
277                                         goto out;
278                                 }
279                         }
280                 }
281                 brelse(bh);
282                 blknr += 1;
283         } while (--iterations > 0);
284 out:
285         return inode;
286 }
287
288 /*
289  * Read the FAT to find the next cluster in the chain
290  * corresponding to 'search_clus'.
291  */
292 static int fat_read_next_clus(struct super_block *sb, int search_clus)
293 {
294         struct msdos_sb_info *sbi = MSDOS_SB(sb);
295         /*bits 31 to 7 give relative sector number*/
296         sector_t blknr = search_clus >> 7;
297         /*bits 6 to 0 give offset*/
298         unsigned int offset = search_clus & 0x7F;
299         __le32 *address;
300         unsigned int next_cluster;
301         struct buffer_head *bh = sb_bread(sb, blknr + sbi->fat_start);
302         if (!bh) {
303                 fat_msg(sb, KERN_ERR,
304                         "NFS:unable to read block(%llu) for finding the next cluster in FAT chain",
305                         (llu)blknr);
306                 return -EIO;
307         }
308         address = (__le32 *) bh->b_data;
309         next_cluster = (le32_to_cpu(address[offset])) & 0x0FFFFFFF;
310         brelse(bh);
311         return next_cluster;
312 }
313
314 /*
315  * Rebuild the parent for a directory that is not connected
316  * to the filesystem root
317  */
318 static
319 struct inode *fat_rebuild_parent(struct super_block *sb, int parent_logstart)
320 {
321         int search_clus, clus_to_match;
322         struct msdos_dir_entry *de;
323         struct inode *parent;
324         struct msdos_sb_info *sbi = MSDOS_SB(sb);
325         sector_t blknr = fat_clus_to_blknr(sbi, parent_logstart);
326         struct buffer_head *parent_bh = sb_bread(sb, blknr);
327
328         if (!parent_bh) {
329                 fat_msg(sb, KERN_ERR,
330                         "NFS:unable to read cluster of parent directory");
331                 return NULL;
332         }
333         de = (struct msdos_dir_entry *) parent_bh->b_data;
334         clus_to_match = fat_get_start(sbi, &de[0]);
335         search_clus = fat_get_start(sbi, &de[1]);
336         if (!search_clus)
337                 search_clus = sbi->root_cluster;
338         brelse(parent_bh);
339         do {
340                 parent =  fat_traverse_cluster(sb,
341                                         search_clus, clus_to_match);
342                 if (IS_ERR(parent) || parent)
343                         break;
344                 search_clus = fat_read_next_clus(sb, search_clus);
345                 if (search_clus < 0)
346                         break;
347         } while (search_clus != FAT_ENT_EOF);
348
349         return parent;
350
351
352 }
353 /*
354  * Find the parent for a directory that is not currently connected to
355  * the filesystem root.
356  *
357  * On entry, the caller holds child_dir->d_inode->i_mutex.
358  */
359 struct dentry *fat_get_parent(struct dentry *child_dir)
360 {
361         struct super_block *sb = child_dir->d_sb;
362         struct buffer_head *dotdot_bh = NULL;
363         struct msdos_dir_entry *de;
364         struct inode *parent_inode = NULL;
365         struct msdos_sb_info *sbi = MSDOS_SB(sb);
366         int parent_logstart;
367
368         if (!fat_get_dotdot_entry(child_dir->d_inode, &dotdot_bh, &de)) {
369                 parent_logstart = fat_get_start(sbi, de);
370                 parent_inode = fat_dget(sb, parent_logstart);
371                 if (!parent_inode && sbi->options.nfs == FAT_NFS_NOSTALE_RO)
372                         parent_inode = fat_rebuild_parent(sb, parent_logstart);
373         }
374         brelse(dotdot_bh);
375
376         return d_obtain_alias(parent_inode);
377 }
378
379 const struct export_operations fat_export_ops = {
380         .fh_to_dentry   = fat_fh_to_dentry,
381         .fh_to_parent   = fat_fh_to_parent,
382         .get_parent     = fat_get_parent,
383 };
384 const struct export_operations fat_export_ops_nostale = {
385         .encode_fh      = fat_encode_fh_nostale,
386         .fh_to_dentry   = fat_fh_to_dentry_nostale,
387         .fh_to_parent   = fat_fh_to_parent_nostale,
388         .get_parent     = fat_get_parent,
389 };