aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorNamjae Jeon <namjae.jeon@samsung.com>2013-04-29 19:21:14 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-04-29 21:28:41 -0400
commitf1e6fb0ab451dae8523fbb8c119a653b2730e938 (patch)
treebf966c2863b2523081fb69f619b7910efd28dc6f /fs
parent8fceb4e0171f6bf64db756c65b2ce5f15aed8b4d (diff)
fat (exportfs): rebuild directory-inode if fat_dget()
This patch enables rebuilding of directory inodes which are not present in the cache.This is done by traversing the disk clusters to find the directory entry of the parent directory and using its i_pos to build the inode. The traversal is done by fat_scan_logstart() which is similar to fat_scan() but matches i_pos values instead of names.fat_scan_logstart() needs an inode parameter to work, for which a dummy inode is created by it's caller fat_rebuild_parent(). This dummy inode is destroyed after the traversal completes. All this is done only if the nostale_ro nfs mount option is specified. Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com> Signed-off-by: Ravishankar N <ravi.n1@samsung.com> Signed-off-by: Amit Sahrawat <a.sahrawat@samsung.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')
-rw-r--r--fs/fat/dir.c23
-rw-r--r--fs/fat/fat.h3
-rw-r--r--fs/fat/inode.c2
-rw-r--r--fs/fat/nfs.c52
4 files changed, 78 insertions, 2 deletions
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
index 165012ef363a..7a6f02caf286 100644
--- a/fs/fat/dir.c
+++ b/fs/fat/dir.c
@@ -964,6 +964,29 @@ int fat_scan(struct inode *dir, const unsigned char *name,
964} 964}
965EXPORT_SYMBOL_GPL(fat_scan); 965EXPORT_SYMBOL_GPL(fat_scan);
966 966
967/*
968 * Scans a directory for a given logstart.
969 * Returns an error code or zero.
970 */
971int fat_scan_logstart(struct inode *dir, int i_logstart,
972 struct fat_slot_info *sinfo)
973{
974 struct super_block *sb = dir->i_sb;
975
976 sinfo->slot_off = 0;
977 sinfo->bh = NULL;
978 while (fat_get_short_entry(dir, &sinfo->slot_off, &sinfo->bh,
979 &sinfo->de) >= 0) {
980 if (fat_get_start(MSDOS_SB(sb), sinfo->de) == i_logstart) {
981 sinfo->slot_off -= sizeof(*sinfo->de);
982 sinfo->nr_slots = 1;
983 sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de);
984 return 0;
985 }
986 }
987 return -ENOENT;
988}
989
967static int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots) 990static int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots)
968{ 991{
969 struct super_block *sb = dir->i_sb; 992 struct super_block *sb = dir->i_sb;
diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index 413eaaf30ed5..21664fcf3616 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -296,6 +296,8 @@ extern int fat_dir_empty(struct inode *dir);
296extern int fat_subdirs(struct inode *dir); 296extern int fat_subdirs(struct inode *dir);
297extern int fat_scan(struct inode *dir, const unsigned char *name, 297extern int fat_scan(struct inode *dir, const unsigned char *name,
298 struct fat_slot_info *sinfo); 298 struct fat_slot_info *sinfo);
299extern int fat_scan_logstart(struct inode *dir, int i_logstart,
300 struct fat_slot_info *sinfo);
299extern int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh, 301extern int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh,
300 struct msdos_dir_entry **de); 302 struct msdos_dir_entry **de);
301extern int fat_alloc_new_dir(struct inode *dir, struct timespec *ts); 303extern int fat_alloc_new_dir(struct inode *dir, struct timespec *ts);
@@ -373,6 +375,7 @@ extern struct inode *fat_build_inode(struct super_block *sb,
373extern int fat_sync_inode(struct inode *inode); 375extern int fat_sync_inode(struct inode *inode);
374extern int fat_fill_super(struct super_block *sb, void *data, int silent, 376extern int fat_fill_super(struct super_block *sb, void *data, int silent,
375 int isvfat, void (*setup)(struct super_block *)); 377 int isvfat, void (*setup)(struct super_block *));
378extern int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de);
376 379
377extern int fat_flush_inodes(struct super_block *sb, struct inode *i1, 380extern int fat_flush_inodes(struct super_block *sb, struct inode *i1,
378 struct inode *i2); 381 struct inode *i2);
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 971ba7d549da..4ff901632b26 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -384,7 +384,7 @@ static int fat_calc_dir_size(struct inode *inode)
384} 384}
385 385
386/* doesn't deal with root inode */ 386/* doesn't deal with root inode */
387static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) 387int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
388{ 388{
389 struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); 389 struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
390 int error; 390 int error;
diff --git a/fs/fat/nfs.c b/fs/fat/nfs.c
index 0748196889b2..93e14933dcb6 100644
--- a/fs/fat/nfs.c
+++ b/fs/fat/nfs.c
@@ -216,6 +216,53 @@ static struct dentry *fat_fh_to_parent_nostale(struct super_block *sb,
216} 216}
217 217
218/* 218/*
219 * Rebuild the parent for a directory that is not connected
220 * to the filesystem root
221 */
222static
223struct inode *fat_rebuild_parent(struct super_block *sb, int parent_logstart)
224{
225 int search_clus, clus_to_match;
226 struct msdos_dir_entry *de;
227 struct inode *parent = NULL;
228 struct inode *dummy_grand_parent = NULL;
229 struct fat_slot_info sinfo;
230 struct msdos_sb_info *sbi = MSDOS_SB(sb);
231 sector_t blknr = fat_clus_to_blknr(sbi, parent_logstart);
232 struct buffer_head *parent_bh = sb_bread(sb, blknr);
233 if (!parent_bh) {
234 fat_msg(sb, KERN_ERR,
235 "unable to read cluster of parent directory");
236 return NULL;
237 }
238
239 de = (struct msdos_dir_entry *) parent_bh->b_data;
240 clus_to_match = fat_get_start(sbi, &de[0]);
241 search_clus = fat_get_start(sbi, &de[1]);
242
243 dummy_grand_parent = fat_dget(sb, search_clus);
244 if (!dummy_grand_parent) {
245 dummy_grand_parent = new_inode(sb);
246 if (!dummy_grand_parent) {
247 brelse(parent_bh);
248 return parent;
249 }
250
251 dummy_grand_parent->i_ino = iunique(sb, MSDOS_ROOT_INO);
252 fat_fill_inode(dummy_grand_parent, &de[1]);
253 MSDOS_I(dummy_grand_parent)->i_pos = -1;
254 }
255
256 if (!fat_scan_logstart(dummy_grand_parent, clus_to_match, &sinfo))
257 parent = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
258
259 brelse(parent_bh);
260 iput(dummy_grand_parent);
261
262 return parent;
263}
264
265/*
219 * Find the parent for a directory that is not currently connected to 266 * Find the parent for a directory that is not currently connected to
220 * the filesystem root. 267 * the filesystem root.
221 * 268 *
@@ -227,10 +274,13 @@ static struct dentry *fat_get_parent(struct dentry *child_dir)
227 struct buffer_head *bh = NULL; 274 struct buffer_head *bh = NULL;
228 struct msdos_dir_entry *de; 275 struct msdos_dir_entry *de;
229 struct inode *parent_inode = NULL; 276 struct inode *parent_inode = NULL;
277 struct msdos_sb_info *sbi = MSDOS_SB(sb);
230 278
231 if (!fat_get_dotdot_entry(child_dir->d_inode, &bh, &de)) { 279 if (!fat_get_dotdot_entry(child_dir->d_inode, &bh, &de)) {
232 int parent_logstart = fat_get_start(MSDOS_SB(sb), de); 280 int parent_logstart = fat_get_start(sbi, de);
233 parent_inode = fat_dget(sb, parent_logstart); 281 parent_inode = fat_dget(sb, parent_logstart);
282 if (!parent_inode && sbi->options.nfs == FAT_NFS_NOSTALE_RO)
283 parent_inode = fat_rebuild_parent(sb, parent_logstart);
234 } 284 }
235 brelse(bh); 285 brelse(bh);
236 286