From: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>

Use "struct fat_slot_info" for fat_scan().  But ".." entry can not provide
valid informations for inode, so add the fat_get_dotdot_entry() as special
case.

Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/fs/fat/dir.c             |   41 ++++++++++++++-----
 25-akpm/fs/fat/inode.c           |    2 
 25-akpm/fs/msdos/namei.c         |   84 +++++++++++++++++++++------------------
 25-akpm/fs/vfat/namei.c          |   16 ++-----
 25-akpm/include/linux/msdos_fs.h |    5 +-
 5 files changed, 88 insertions(+), 60 deletions(-)

diff -puN fs/fat/dir.c~fat-use-struct-fat_slot_info-for-fat_scan fs/fat/dir.c
--- 25/fs/fat/dir.c~fat-use-struct-fat_slot_info-for-fat_scan	Sun Mar  6 17:13:15 2005
+++ 25-akpm/fs/fat/dir.c	Sun Mar  6 17:13:15 2005
@@ -510,9 +510,9 @@ ParseLong:
 	j = last_u;
 
 	lpos = cpos - (long_slots+1)*sizeof(struct msdos_dir_entry);
-	if (!memcmp(de->name,MSDOS_DOT,11))
+	if (!memcmp(de->name, MSDOS_DOT, MSDOS_NAME))
 		inum = inode->i_ino;
-	else if (!memcmp(de->name,MSDOS_DOTDOT,11)) {
+	else if (!memcmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) {
 		inum = parent_ino(filp->f_dentry);
 	} else {
 		struct inode *tmp = fat_iget(sb, i_pos);
@@ -695,6 +695,26 @@ static int fat_get_short_entry(struct in
 	return -ENOENT;
 }
 
+/*
+ * The ".." entry can not provide the "struct fat_slot_info" informations
+ * for inode. So, this function provide the some informations only.
+ */
+int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh,
+			 struct msdos_dir_entry **de, loff_t *i_pos)
+{
+	loff_t offset;
+
+	offset = 0;
+	*bh = NULL;
+	while (fat_get_short_entry(dir, &offset, bh, de, i_pos) >= 0) {
+		if (!strncmp((*de)->name, MSDOS_DOTDOT, MSDOS_NAME))
+			return 0;
+	}
+	return -ENOENT;
+}
+
+EXPORT_SYMBOL(fat_get_dotdot_entry);
+
 /* See if directory is empty */
 int fat_dir_empty(struct inode *dir)
 {
@@ -744,16 +764,17 @@ int fat_subdirs(struct inode *dir)
  * Returns an error code or zero.
  */
 int fat_scan(struct inode *dir, const unsigned char *name,
-	     struct buffer_head **bh, struct msdos_dir_entry **de,
-	     loff_t *i_pos)
+	     struct fat_slot_info *sinfo)
 {
-	loff_t cpos;
-
-	*bh = NULL;
-	cpos = 0;
-	while (fat_get_short_entry(dir, &cpos, bh, de, i_pos) >= 0) {
-		if (!strncmp((*de)->name, name, MSDOS_NAME))
+	sinfo->slot_off = 0;
+	sinfo->bh = NULL;
+	while (fat_get_short_entry(dir, &sinfo->slot_off, &sinfo->bh,
+				   &sinfo->de, &sinfo->i_pos) >= 0) {
+		if (!strncmp(sinfo->de->name, name, MSDOS_NAME)) {
+			sinfo->slot_off -= sizeof(*sinfo->de);
+			sinfo->nr_slots = 1;
 			return 0;
+		}
 	}
 	return -ENOENT;
 }
diff -puN fs/fat/inode.c~fat-use-struct-fat_slot_info-for-fat_scan fs/fat/inode.c
--- 25/fs/fat/inode.c~fat-use-struct-fat_slot_info-for-fat_scan	Sun Mar  6 17:13:15 2005
+++ 25-akpm/fs/fat/inode.c	Sun Mar  6 17:13:15 2005
@@ -654,7 +654,7 @@ static struct dentry *fat_get_parent(str
 
 	lock_kernel();
 
-	err = fat_scan(child->d_inode, MSDOS_DOTDOT, &bh, &de, &i_pos);
+	err = fat_get_dotdot_entry(child->d_inode, &bh, &de, &i_pos);
 	if (err) {
 		parent = ERR_PTR(err);
 		goto out;
diff -puN fs/msdos/namei.c~fat-use-struct-fat_slot_info-for-fat_scan fs/msdos/namei.c
--- 25/fs/msdos/namei.c~fat-use-struct-fat_slot_info-for-fat_scan	Sun Mar  6 17:13:15 2005
+++ 25-akpm/fs/msdos/namei.c	Sun Mar  6 17:13:15 2005
@@ -140,26 +140,33 @@ static int msdos_find(struct inode *dir,
 		      struct buffer_head **bh, struct msdos_dir_entry **de,
 		      loff_t *i_pos)
 {
+	struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
+	struct fat_slot_info sinfo;
 	unsigned char msdos_name[MSDOS_NAME];
-	char dotsOK;
-	int res;
+	int err;
 
-	dotsOK = MSDOS_SB(dir->i_sb)->options.dotsOK;
-	res = msdos_format_name(name, len, msdos_name,
-				&MSDOS_SB(dir->i_sb)->options);
-	if (res < 0)
+	err = msdos_format_name(name, len, msdos_name, &sbi->options);
+	if (err)
 		return -ENOENT;
-	res = fat_scan(dir, msdos_name, bh, de, i_pos);
-	if (!res && dotsOK) {
+
+	err = fat_scan(dir, msdos_name, &sinfo);
+	if (!err && sbi->options.dotsOK) {
 		if (name[0] == '.') {
-			if (!((*de)->attr & ATTR_HIDDEN))
-				res = -ENOENT;
+			if (!(sinfo.de->attr & ATTR_HIDDEN))
+				err = -ENOENT;
 		} else {
-			if ((*de)->attr & ATTR_HIDDEN)
-				res = -ENOENT;
+			if (sinfo.de->attr & ATTR_HIDDEN)
+				err = -ENOENT;
 		}
+		if (err)
+			brelse(sinfo.bh);
 	}
-	return res;
+	if (!err) {
+		*i_pos = sinfo.i_pos;
+		*de = sinfo.de;
+		*bh = sinfo.bh;
+	}
+	return err;
 }
 
 /*
@@ -289,6 +296,7 @@ static int msdos_create(struct inode *di
 			struct nameidata *nd)
 {
 	struct super_block *sb = dir->i_sb;
+	struct fat_slot_info sinfo;
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
 	struct inode *inode;
@@ -305,18 +313,18 @@ static int msdos_create(struct inode *di
 	}
 	is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
 	/* Have to do it due to foo vs. .foo conflicts */
-	if (fat_scan(dir, msdos_name, &bh, &de, &i_pos) >= 0) {
-		brelse(bh);
+	if (!fat_scan(dir, msdos_name, &sinfo)) {
+		brelse(sinfo.bh);
 		unlock_kernel();
 		return -EINVAL;
 	}
-	inode = NULL;
+
 	res = msdos_add_entry(dir, msdos_name, &bh, &de, &i_pos, 0, is_hid);
 	if (res) {
 		unlock_kernel();
 		return res;
 	}
-	inode = fat_build_inode(dir->i_sb, de, i_pos);
+	inode = fat_build_inode(sb, de, i_pos);
 	brelse(bh);
 	if (IS_ERR(inode)) {
 		unlock_kernel();
@@ -372,6 +380,7 @@ rmdir_done:
 static int msdos_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 {
 	struct super_block *sb = dir->i_sb;
+	struct fat_slot_info sinfo;
 	struct buffer_head *bh;
 	struct msdos_dir_entry *de;
 	struct inode *inode;
@@ -388,8 +397,11 @@ static int msdos_mkdir(struct inode *dir
 	}
 	is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
 	/* foo vs .foo situation */
-	if (fat_scan(dir, msdos_name, &bh, &de, &i_pos) >= 0)
-		goto out_exist;
+	if (!fat_scan(dir, msdos_name, &sinfo)) {
+		brelse(sinfo.bh);
+		res = -EINVAL;
+		goto out_unlock;
+	}
 
 	res = msdos_add_entry(dir, msdos_name, &bh, &de, &i_pos, 1, is_hid);
 	if (res)
@@ -400,7 +412,6 @@ static int msdos_mkdir(struct inode *dir
 		res = PTR_ERR(inode);
 		goto out_unlock;
 	}
-	res = 0;
 
 	dir->i_nlink++;
 	inode->i_nlink = 2;	/* no need to mark them dirty */
@@ -408,7 +419,6 @@ static int msdos_mkdir(struct inode *dir
 	res = fat_new_dir(inode, dir, 0);
 	if (res)
 		goto mkdir_error;
-
 	brelse(bh);
 	d_instantiate(dentry, inode);
 	res = 0;
@@ -429,11 +439,6 @@ mkdir_error:
 	fat_detach(inode);
 	iput(inode);
 	goto out_unlock;
-
-out_exist:
-	brelse(bh);
-	res = -EINVAL;
-	goto out_unlock;
 }
 
 /***** Unlink a file */
@@ -474,6 +479,7 @@ static int do_msdos_rename(struct inode 
 			   struct msdos_dir_entry *old_de, loff_t old_i_pos,
 			   int is_hid)
 {
+	struct fat_slot_info sinfo;
 	struct buffer_head *new_bh = NULL, *dotdot_bh = NULL;
 	struct msdos_dir_entry *new_de, *dotdot_de;
 	struct inode *old_inode, *new_inode;
@@ -485,17 +491,22 @@ static int do_msdos_rename(struct inode 
 	new_inode = new_dentry->d_inode;
 	is_dir = S_ISDIR(old_inode->i_mode);
 
-	if (fat_scan(new_dir, new_name, &new_bh, &new_de, &new_i_pos) >= 0 &&
-	    !new_inode)
-		goto degenerate_case;
+	error = fat_scan(new_dir, new_name, &sinfo);
+	if (!error) {
+		new_i_pos = sinfo.i_pos;
+		new_bh = sinfo.bh;
+		new_de = sinfo.de;
+		if (!new_inode)
+			goto degenerate_case;
+	}
 	if (is_dir) {
 		if (new_inode) {
 			error = fat_dir_empty(new_inode);
 			if (error)
 				goto out;
 		}
-		if (fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh,
-			     &dotdot_de, &dotdot_i_pos) < 0) {
+		if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de,
+					 &dotdot_i_pos) < 0) {
 			error = -EIO;
 			goto out;
 		}
@@ -554,6 +565,7 @@ degenerate_case:
 	error = -EINVAL;
 	if (new_de != old_de)
 		goto out;
+	brelse(new_bh);
 	if (is_hid)
 		MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
 	else
@@ -569,9 +581,7 @@ degenerate_case:
 static int msdos_rename(struct inode *old_dir, struct dentry *old_dentry,
 			struct inode *new_dir, struct dentry *new_dentry)
 {
-	struct buffer_head *old_bh;
-	struct msdos_dir_entry *old_de;
-	loff_t old_i_pos;
+	struct fat_slot_info sinfo;
 	int error, is_hid, old_hid; /* if new file and old file are hidden */
 	unsigned char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
 
@@ -592,14 +602,14 @@ static int msdos_rename(struct inode *ol
 	old_hid =
 	     (old_dentry->d_name.name[0] == '.') && (old_msdos_name[0] != '.');
 
-	error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de, &old_i_pos);
+	error = fat_scan(old_dir, old_msdos_name, &sinfo);
 	if (error < 0)
 		goto rename_done;
 
 	error = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
 				new_dir, new_msdos_name, new_dentry,
-				old_bh, old_de, old_i_pos, is_hid);
-	brelse(old_bh);
+				sinfo.bh, sinfo.de, sinfo.i_pos, is_hid);
+	brelse(sinfo.bh);
 
 rename_done:
 	unlock_kernel();
diff -puN fs/vfat/namei.c~fat-use-struct-fat_slot_info-for-fat_scan fs/vfat/namei.c
--- 25/fs/vfat/namei.c~fat-use-struct-fat_slot_info-for-fat_scan	Sun Mar  6 17:13:15 2005
+++ 25-akpm/fs/vfat/namei.c	Sun Mar  6 17:13:15 2005
@@ -208,15 +208,11 @@ static int vfat_valid_longname(const uns
 
 static int vfat_find_form(struct inode *dir, unsigned char *name)
 {
-	struct msdos_dir_entry *de;
-	struct buffer_head *bh = NULL;
-	loff_t i_pos;
-	int res;
-
-	res = fat_scan(dir, name, &bh, &de, &i_pos);
-	brelse(bh);
-	if (res < 0)
+	struct fat_slot_info sinfo;
+	int err = fat_scan(dir, name, &sinfo);
+	if (err)
 		return -ENOENT;
+	brelse(sinfo.bh);
 	return 0;
 }
 
@@ -938,8 +934,8 @@ static int vfat_rename(struct inode *old
 
 	is_dir = S_ISDIR(old_inode->i_mode);
 	if (is_dir) {
-		if (fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh,
-			     &dotdot_de, &dotdot_i_pos) < 0) {
+		if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de,
+					 &dotdot_i_pos) < 0) {
 			err = -EIO;
 			goto out;
 		}
diff -puN include/linux/msdos_fs.h~fat-use-struct-fat_slot_info-for-fat_scan include/linux/msdos_fs.h
--- 25/include/linux/msdos_fs.h~fat-use-struct-fat_slot_info-for-fat_scan	Sun Mar  6 17:13:15 2005
+++ 25-akpm/include/linux/msdos_fs.h	Sun Mar  6 17:13:15 2005
@@ -331,8 +331,9 @@ extern int fat_new_dir(struct inode *dir
 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 buffer_head **res_bh,
-		    struct msdos_dir_entry **res_de, loff_t *i_pos);
+		    struct fat_slot_info *sinfo);
+extern int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh,
+				struct msdos_dir_entry **de, loff_t *i_pos);
 extern int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo);
 
 /* fat/fatent.c */
_