Re: [PATCH v3 4/5] fat (exportfs): rebuild directory-inode if fat_dget() fails

From: OGAWA Hirofumi
Date: Sat Sep 22 2012 - 07:49:24 EST


Namjae Jeon <linkinjeon@xxxxxxxxx> writes:

> From: Namjae Jeon <namjae.jeon@xxxxxxxxxxx>
>
> 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.

Hmm... Is this functionality is necessary to work correctly? If we
didn't provide this, which case do server return ESTALE?

Well, this looks like duplication of lookup, we may need to consolidate.

> Signed-off-by: Namjae Jeon <namjae.jeon@xxxxxxxxxxx>
> Signed-off-by: Ravishankar N <ravi.n1@xxxxxxxxxxx>
> Signed-off-by: Amit Sahrawat <a.sahrawat@xxxxxxxxxxx>
> ---
> fs/fat/nfs.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 128 insertions(+), 4 deletions(-)
>
> diff --git a/fs/fat/nfs.c b/fs/fat/nfs.c
> index 3cf5412..bb4262c 100644
> --- a/fs/fat/nfs.c
> +++ b/fs/fat/nfs.c
> @@ -104,6 +104,93 @@ struct dentry *fat_fh_to_parent(struct super_block *sb, struct fid *fid,
> }
>
> /*
> + * Read the directory entries of 'search_clus' and find the entry
> + * which contains 'match_ipos' for the starting cluster.If the entry
> + * is found, rebuild its inode.
> + */
> +static struct inode *fat_traverse_cluster(struct super_block *sb,
> + int search_clus, int match_ipos)
> +{
> + struct msdos_sb_info *sbi = MSDOS_SB(sb);
> + struct buffer_head *bh;
> + sector_t blknr;
> + int parent_ipos, search_ipos;
> + int i;
> + struct msdos_dir_entry *de;
> + struct inode *inode = NULL;
> + int iterations = sbi->cluster_size >> sb->s_blocksize_bits;
> + blknr = fat_clus_to_blknr(sbi, search_clus);
> +
> + do {
> + bh = sb_bread(sb, blknr);
> + if (!bh) {
> + fat_msg(sb, KERN_ERR,
> + "NFS:unable to read block(%llu) while traversing cluster(%d)",
> + (llu)blknr, search_clus);
> + inode = ERR_PTR(-EIO);
> + goto out;
> + }
> + de = (struct msdos_dir_entry *)bh->b_data;
> + for (i = 0; i < sbi->dir_per_block; i++) {
> + if (de[i].name[0] == FAT_ENT_FREE) {
> + /*Reached end of directory*/
> + brelse(bh);
> + inode = ERR_PTR(-ENODATA);
> + goto out;
> + }
> + if (de[i].name[0] == DELETED_FLAG)
> + continue;
> + if (de[i].attr == ATTR_EXT)
> + continue;
> + if (!(de[i].attr & ATTR_DIR))
> + continue;
> + else {
> + search_ipos = fat_get_start(sbi, &de[i]);
> + if (search_ipos == match_ipos) {
> + /*Success.Now build the inode*/
> + parent_ipos = (loff_t)i +
> + (blknr << sbi->dir_per_block_bits);
> + inode = fat_build_inode(sb, &de[i],
> + parent_ipos);
> + brelse(bh);
> + goto out;
> + }
> + }
> + }
> + brelse(bh);
> + blknr += 1;
> + } while (--iterations > 0);
> +out:
> + return inode;
> +}
> +
> +/*
> + * Read the FAT to find the next cluster in the chain
> + * corresponding to 'search_clus'.
> + */
> +static int fat_read_next_clus(struct super_block *sb, int search_clus)
> +{
> + struct msdos_sb_info *sbi = MSDOS_SB(sb);
> + /*bits 31 to 7 give relative sector number*/
> + sector_t blknr = search_clus >> 7;
> + /*bits 6 to 0 give offset*/
> + unsigned int offset = search_clus & 0x7F;
> + __le32 *address;
> + unsigned int next_cluster;
> + struct buffer_head *bh = sb_bread(sb, blknr + sbi->fat_start);
> + if (!bh) {
> + fat_msg(sb, KERN_ERR,
> + "NFS:unable to read block(%llu) for finding the next cluster in FAT chain",
> + (llu)blknr);
> + return -EIO;
> + }
> + address = (__le32 *) bh->b_data;
> + next_cluster = (le32_to_cpu(address[offset])) & 0x0FFFFFFF;
> + brelse(bh);
> + return next_cluster;
> +}
> +
> +/*
> * Find the parent for a directory that is not currently connected to
> * the filesystem root.
> *
> @@ -112,15 +199,52 @@ struct dentry *fat_fh_to_parent(struct super_block *sb, struct fid *fid,
> struct dentry *fat_get_parent(struct dentry *child_dir)
> {
> struct super_block *sb = child_dir->d_sb;
> - struct buffer_head *bh = NULL;
> + struct buffer_head *dotdot_bh = NULL, *parent_bh = NULL;
> struct msdos_dir_entry *de;
> struct inode *parent_inode = NULL;
> + struct msdos_sb_info *sbi = MSDOS_SB(sb);
> + int parent_logstart;
> + int search_clus, clus_to_match;
> + sector_t blknr;
>
> - if (!fat_get_dotdot_entry(child_dir->d_inode, &bh, &de)) {
> - int parent_logstart = fat_get_start(MSDOS_SB(sb), de);
> + if (!fat_get_dotdot_entry(child_dir->d_inode, &dotdot_bh, &de)) {
> + parent_logstart = fat_get_start(sbi, de);
> parent_inode = fat_dget(sb, parent_logstart);
> + if (parent_inode || sbi->options.nfs != FAT_NFS_LIMITED)
> + goto out;
> + if (!parent_logstart)
> + /*logstart of dotdot entry is zero if
> + * if the directory's parent is root
> + */
> + parent_inode = sb->s_root->d_inode;
> + else {
> + blknr = fat_clus_to_blknr(sbi, parent_logstart);
> + parent_bh = sb_bread(sb, blknr);
> + if (!parent_bh) {
> + fat_msg(sb, KERN_ERR,
> + "NFS:unable to read cluster of parent directory");
> + goto out;
> + }
> + 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]);
> + if (!search_clus)
> + search_clus = sbi->root_cluster;
> + brelse(parent_bh);
> + do {
> + parent_inode = fat_traverse_cluster(sb,
> + search_clus, clus_to_match);
> + if (IS_ERR(parent_inode) || parent_inode)
> + break;
> + search_clus = fat_read_next_clus(sb,
> + search_clus);
> + if (search_clus < 0)
> + break;
> + } while (search_clus != FAT_ENT_EOF);
> + }
> }
> - brelse(bh);
> +out:
> + brelse(dotdot_bh);
>
> return d_obtain_alias(parent_inode);
> }

--
OGAWA Hirofumi <hirofumi@xxxxxxxxxxxxxxxxxx>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/