return d_obtain_alias(inode);
}
+/*
+ * 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.
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_NOSTALE_RO)
+ 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);
}