]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
fat (exportfs): rebuild directory-inode if fat_dget()
authorNamjae Jeon <namjae.jeon@samsung.com>
Wed, 20 Mar 2013 04:08:26 +0000 (15:08 +1100)
committerStephen Rothwell <sfr@canb.auug.org.au>
Thu, 21 Mar 2013 05:34:26 +0000 (16:34 +1100)
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>
fs/fat/dir.c
fs/fat/fat.h
fs/fat/inode.c
fs/fat/nfs.c

index 165012ef363aafc77c23c839b5c72fb02c7db0b4..7a6f02caf286d4fa20990b259dd5e4773eeff96e 100644 (file)
@@ -964,6 +964,29 @@ int fat_scan(struct inode *dir, const unsigned char *name,
 }
 EXPORT_SYMBOL_GPL(fat_scan);
 
+/*
+ * Scans a directory for a given logstart.
+ * Returns an error code or zero.
+ */
+int fat_scan_logstart(struct inode *dir, int i_logstart,
+                     struct fat_slot_info *sinfo)
+{
+       struct super_block *sb = dir->i_sb;
+
+       sinfo->slot_off = 0;
+       sinfo->bh = NULL;
+       while (fat_get_short_entry(dir, &sinfo->slot_off, &sinfo->bh,
+                                  &sinfo->de) >= 0) {
+               if (fat_get_start(MSDOS_SB(sb), sinfo->de) == i_logstart) {
+                       sinfo->slot_off -= sizeof(*sinfo->de);
+                       sinfo->nr_slots = 1;
+                       sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de);
+                       return 0;
+               }
+       }
+       return -ENOENT;
+}
+
 static int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots)
 {
        struct super_block *sb = dir->i_sb;
index 413eaaf30ed57f68bcefc8bf4aa354df766a4e60..21664fcf361673ec263ce2a23b59c3d7bdabf735 100644 (file)
@@ -296,6 +296,8 @@ extern int fat_dir_empty(struct inode *dir);
 extern int fat_subdirs(struct inode *dir);
 extern int fat_scan(struct inode *dir, const unsigned char *name,
                    struct fat_slot_info *sinfo);
+extern int fat_scan_logstart(struct inode *dir, int i_logstart,
+                            struct fat_slot_info *sinfo);
 extern int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh,
                                struct msdos_dir_entry **de);
 extern 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,
 extern int fat_sync_inode(struct inode *inode);
 extern int fat_fill_super(struct super_block *sb, void *data, int silent,
                          int isvfat, void (*setup)(struct super_block *));
+extern int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de);
 
 extern int fat_flush_inodes(struct super_block *sb, struct inode *i1,
                            struct inode *i2);
index 971ba7d549da4960032d43ae7d7e507a607a9874..4ff901632b26a194790054d0f29a94f633c6f8c3 100644 (file)
@@ -384,7 +384,7 @@ static int fat_calc_dir_size(struct inode *inode)
 }
 
 /* doesn't deal with root inode */
-static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
+int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
 {
        struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
        int error;
index 0748196889b2591faf50abd922a26fcc330303d1..93e14933dcb6fc2abc24462fe9965a070e6d234d 100644 (file)
@@ -215,6 +215,53 @@ static struct dentry *fat_fh_to_parent_nostale(struct super_block *sb,
        return d_obtain_alias(inode);
 }
 
+/*
+ * Rebuild the parent for a directory that is not connected
+ *  to the filesystem root
+ */
+static
+struct inode *fat_rebuild_parent(struct super_block *sb, int parent_logstart)
+{
+       int search_clus, clus_to_match;
+       struct msdos_dir_entry *de;
+       struct inode *parent = NULL;
+       struct inode *dummy_grand_parent = NULL;
+       struct fat_slot_info sinfo;
+       struct msdos_sb_info *sbi = MSDOS_SB(sb);
+       sector_t blknr = fat_clus_to_blknr(sbi, parent_logstart);
+       struct buffer_head *parent_bh = sb_bread(sb, blknr);
+       if (!parent_bh) {
+               fat_msg(sb, KERN_ERR,
+                       "unable to read cluster of parent directory");
+               return NULL;
+       }
+
+       de = (struct msdos_dir_entry *) parent_bh->b_data;
+       clus_to_match = fat_get_start(sbi, &de[0]);
+       search_clus = fat_get_start(sbi, &de[1]);
+
+       dummy_grand_parent = fat_dget(sb, search_clus);
+       if (!dummy_grand_parent) {
+               dummy_grand_parent = new_inode(sb);
+               if (!dummy_grand_parent) {
+                       brelse(parent_bh);
+                       return parent;
+               }
+
+               dummy_grand_parent->i_ino = iunique(sb, MSDOS_ROOT_INO);
+               fat_fill_inode(dummy_grand_parent, &de[1]);
+               MSDOS_I(dummy_grand_parent)->i_pos = -1;
+       }
+
+       if (!fat_scan_logstart(dummy_grand_parent, clus_to_match, &sinfo))
+               parent = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
+
+       brelse(parent_bh);
+       iput(dummy_grand_parent);
+
+       return parent;
+}
+
 /*
  * Find the parent for a directory that is not currently connected to
  * the filesystem root.
@@ -227,10 +274,13 @@ static struct dentry *fat_get_parent(struct dentry *child_dir)
        struct buffer_head *bh = NULL;
        struct msdos_dir_entry *de;
        struct inode *parent_inode = NULL;
+       struct msdos_sb_info *sbi = MSDOS_SB(sb);
 
        if (!fat_get_dotdot_entry(child_dir->d_inode, &bh, &de)) {
-               int parent_logstart = fat_get_start(MSDOS_SB(sb), de);
+               int parent_logstart = fat_get_start(sbi, de);
                parent_inode = fat_dget(sb, parent_logstart);
+               if (!parent_inode && sbi->options.nfs == FAT_NFS_NOSTALE_RO)
+                       parent_inode = fat_rebuild_parent(sb, parent_logstart);
        }
        brelse(bh);