lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1341342576-15394-2-git-send-email-steve@digidescorp.com>
Date:	Tue,  3 Jul 2012 14:09:35 -0500
From:	"Steven J. Magnani" <steve@...idescorp.com>
To:	OGAWA Hirofumi <hirofumi@...l.parknet.co.jp>
Cc:	linux-kernel@...r.kernel.org,
	"Steven J. Magnani" <steve@...idescorp.com>
Subject: [PATCH 1/2] fat (exportfs): drop ineffective get_parent code

This patch reorganizes existing code for reuse by the next patch in the series.

The only functional change is to remove ineffective code from 
fat_get_dotdot_entry() and limit fat_get_parent() to immediate children of the
root (the only disconnected directory case that can be handled without a 
search). The limitation will be removed in the next patch in the series.

These changes are a slight improvement over the current situation, in that they
allow reconnection of immediate subdirectories of the root, and reporting of
ESTALE for other cases.

The reason the i_pos code in fat_get_dotdot_entry() is ineffective is
because the value is the on-disk position of a ".." directory entry.
What is needed in order to map NFS file handles back to objects is the on-disk
position of the named entry to which ".." refers.

Signed-off-by: Steven J. Magnani <steve@...idescorp.com>
---
diff -uprN a/fs/fat/dir.c b/fs/fat/dir.c
--- a/fs/fat/dir.c	2012-07-03 07:21:58.918610460 -0500
+++ b/fs/fat/dir.c	2012-07-03 10:07:58.315967863 -0500
@@ -40,14 +40,6 @@ static inline unsigned char fat_tolower(
 	return ((c >= 'A') && (c <= 'Z')) ? c+32 : c;
 }
 
-static inline loff_t fat_make_i_pos(struct super_block *sb,
-				    struct buffer_head *bh,
-				    struct msdos_dir_entry *de)
-{
-	return ((loff_t)bh->b_blocknr << MSDOS_SB(sb)->dir_per_block_bits)
-		| (de - (struct msdos_dir_entry *)bh->b_data);
-}
-
 static inline void fat_dir_readahead(struct inode *dir, sector_t iblock,
 				     sector_t phys)
 {
@@ -876,17 +868,14 @@ static int fat_get_short_entry(struct in
  * 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)
+			 struct msdos_dir_entry **de)
 {
-	loff_t offset;
+	loff_t offset = 0;
 
-	offset = 0;
-	*bh = NULL;
+	*de = NULL;
 	while (fat_get_short_entry(dir, &offset, bh, de) >= 0) {
-		if (!strncmp((*de)->name, MSDOS_DOTDOT, MSDOS_NAME)) {
-			*i_pos = fat_make_i_pos(dir->i_sb, *bh, *de);
+		if (!strncmp((*de)->name, MSDOS_DOTDOT, MSDOS_NAME))
 			return 0;
-		}
 	}
 	return -ENOENT;
 }
diff -uprN a/fs/fat/fat.h b/fs/fat/fat.h
--- a/fs/fat/fat.h	2012-07-03 07:21:53.723637293 -0500
+++ b/fs/fat/fat.h	2012-07-03 10:07:58.325967754 -0500
@@ -169,6 +169,15 @@ static inline umode_t fat_make_mode(stru
 		return (mode & ~sbi->options.fs_fmask) | S_IFREG;
 }
 
+/* Construct i_pos from on-disk position of directory entry */
+static inline loff_t fat_make_i_pos(struct super_block *sb,
+				    struct buffer_head *bh,
+				    struct msdos_dir_entry *de)
+{
+	return ((loff_t)bh->b_blocknr << MSDOS_SB(sb)->dir_per_block_bits)
+		| (de - (struct msdos_dir_entry *)bh->b_data);
+}
+
 /* Return the FAT attribute byte for this inode */
 static inline u8 fat_make_attrs(struct inode *inode)
 {
@@ -262,7 +271,7 @@ 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_get_dotdot_entry(struct inode *dir, struct buffer_head **bh,
-				struct msdos_dir_entry **de, loff_t *i_pos);
+				struct msdos_dir_entry **de);
 extern int fat_alloc_new_dir(struct inode *dir, struct timespec *ts);
 extern int fat_add_entries(struct inode *dir, void *slots, int nr_slots,
 			   struct fat_slot_info *sinfo);
diff -uprN a/fs/fat/inode.c b/fs/fat/inode.c
--- a/fs/fat/inode.c	2012-07-03 07:21:53.733637243 -0500
+++ b/fs/fat/inode.c	2012-07-03 10:07:58.335967643 -0500
@@ -411,31 +411,48 @@ static int fat_fill_inode(struct inode *
 	return 0;
 }
 
+/**
+ * Create inode from specified directory entry.
+ * Do not call this directly unless intentionally bypassing the FAT dir cache.
+ */
+static struct inode *fat_build_unhashed_inode(struct super_block *sb,
+			struct msdos_dir_entry *de)
+{
+	struct inode *inode;
+
+	inode = new_inode(sb);
+	if (inode) {
+		int err;
+
+		inode->i_ino = iunique(sb, MSDOS_ROOT_INO);
+		inode->i_version = 1;
+		err = fat_fill_inode(inode, de);
+		if (err) {
+			iput(inode);
+			inode = ERR_PTR(err);
+		}
+	} else
+		inode = ERR_PTR(-ENOMEM);
+
+	return inode;
+}
+
+/**
+ * Find or create FAT dir cache inode for i_pos.
+ */
 struct inode *fat_build_inode(struct super_block *sb,
 			struct msdos_dir_entry *de, loff_t i_pos)
 {
 	struct inode *inode;
-	int err;
 
 	inode = fat_iget(sb, i_pos);
-	if (inode)
-		goto out;
-	inode = new_inode(sb);
 	if (!inode) {
-		inode = ERR_PTR(-ENOMEM);
-		goto out;
-	}
-	inode->i_ino = iunique(sb, MSDOS_ROOT_INO);
-	inode->i_version = 1;
-	err = fat_fill_inode(inode, de);
-	if (err) {
-		iput(inode);
-		inode = ERR_PTR(err);
-		goto out;
+		inode = fat_build_unhashed_inode(sb, de);
+		if (!IS_ERR(inode)) {
+			fat_attach(inode, i_pos);
+			insert_inode_hash(inode);
+		}
 	}
-	fat_attach(inode, i_pos);
-	insert_inode_hash(inode);
-out:
 	return inode;
 }
 
@@ -677,25 +694,59 @@ static const struct super_operations fat
  * of i_logstart is used to store the directory entry offset.
  */
 
+static int
+fat_encode_fh(struct inode *inode, __u32 *fh, int *lenp, struct inode *parent)
+{
+	int len = *lenp;
+	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
+	loff_t i_pos;
+
+	if (len < 5) {
+		*lenp = 5;
+		return 255; /* no room */
+	}
+
+	i_pos = fat_i_pos_read(sbi, inode);
+	*lenp = 5;
+	fh[0] = inode->i_ino;
+	fh[1] = inode->i_generation;
+	fh[2] = i_pos >> 8;
+	fh[3] = ((i_pos & 0xf0) << 24) | MSDOS_I(inode)->i_logstart;
+	fh[4] = (i_pos & 0x0f) << 28;
+	if (parent)
+		fh[4] |= MSDOS_I(parent)->i_logstart;
+	return 3;
+}
+
+static int fat_is_valid_fh(int fh_len, int fh_type)
+{
+	return ((fh_len >= 5) && (fh_type == 3));
+}
+
 static struct dentry *fat_fh_to_dentry(struct super_block *sb,
 		struct fid *fid, int fh_len, int fh_type)
 {
 	struct inode *inode = NULL;
 	u32 *fh = fid->raw;
+	loff_t i_pos;
+	unsigned long i_ino;
+	__u32 i_generation;
+	int i_logstart;
 
-	if (fh_len < 5 || fh_type != 3)
+	if (!fat_is_valid_fh(fh_len, fh_type))
 		return NULL;
 
-	inode = ilookup(sb, fh[0]);
-	if (!inode || inode->i_generation != fh[1]) {
+	i_ino = fh[0];
+	i_generation = fh[1];
+	i_logstart = fh[3] & 0x0fffffff;
+
+	inode = ilookup(sb, i_ino);
+	if (!inode || inode->i_generation != i_generation) {
 		if (inode)
 			iput(inode);
 		inode = NULL;
 	}
 	if (!inode) {
-		loff_t i_pos;
-		int i_logstart = fh[3] & 0x0fffffff;
-
 		i_pos = (loff_t)fh[2] << 8;
 		i_pos |= ((fh[3] >> 24) & 0xf0) | (fh[4] >> 28);
 
@@ -728,52 +779,28 @@ static struct dentry *fat_fh_to_dentry(s
 	return d_obtain_alias(inode);
 }
 
-static int
-fat_encode_fh(struct inode *inode, __u32 *fh, int *lenp, struct inode *parent)
-{
-	int len = *lenp;
-	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
-	loff_t i_pos;
-
-	if (len < 5) {
-		*lenp = 5;
-		return 255; /* no room */
-	}
-
-	i_pos = fat_i_pos_read(sbi, inode);
-	*lenp = 5;
-	fh[0] = inode->i_ino;
-	fh[1] = inode->i_generation;
-	fh[2] = i_pos >> 8;
-	fh[3] = ((i_pos & 0xf0) << 24) | MSDOS_I(inode)->i_logstart;
-	fh[4] = (i_pos & 0x0f) << 28;
-	if (parent)
-		fh[4] |= MSDOS_I(parent)->i_logstart;
-	return 3;
-}
-
-static struct dentry *fat_get_parent(struct dentry *child)
+/*
+ * NFS support:  Find the parent for a disconnected directory.
+ */
+static struct dentry *fat_get_parent(struct dentry *child_dir)
 {
-	struct super_block *sb = child->d_sb;
-	struct buffer_head *bh;
+	struct super_block *sb = child_dir->d_sb;
+	struct buffer_head *bh = NULL;
 	struct msdos_dir_entry *de;
-	loff_t i_pos;
-	struct dentry *parent;
-	struct inode *inode;
-	int err;
+	struct dentry *parent = ERR_PTR(-ESTALE);
+	int parent_logstart;
 
 	lock_super(sb);
 
-	err = fat_get_dotdot_entry(child->d_inode, &bh, &de, &i_pos);
-	if (err) {
-		parent = ERR_PTR(err);
+	if (fat_get_dotdot_entry(child_dir->d_inode, &bh, &de))
 		goto out;
-	}
-	inode = fat_build_inode(sb, de, i_pos);
-	brelse(bh);
 
-	parent = d_obtain_alias(inode);
+	parent_logstart = fat_get_start(MSDOS_SB(sb), de);
+	if (!parent_logstart)
+		parent = sb->s_root;
+
 out:
+	brelse(bh);
 	unlock_super(sb);
 
 	return parent;
diff -uprN a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
--- a/fs/fat/namei_msdos.c	2012-07-03 07:21:53.750637154 -0500
+++ b/fs/fat/namei_msdos.c	2012-07-03 10:07:58.351967464 -0500
@@ -440,7 +440,7 @@ static int do_msdos_rename(struct inode
 	struct inode *old_inode, *new_inode;
 	struct fat_slot_info old_sinfo, sinfo;
 	struct timespec ts;
-	loff_t dotdot_i_pos, new_i_pos;
+	loff_t new_i_pos;
 	int err, old_attrs, is_dir, update_dotdot, corrupt = 0;
 
 	old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
@@ -456,8 +456,8 @@ static int do_msdos_rename(struct inode
 	is_dir = S_ISDIR(old_inode->i_mode);
 	update_dotdot = (is_dir && old_dir != new_dir);
 	if (update_dotdot) {
-		if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de,
-					 &dotdot_i_pos) < 0) {
+		err = fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de);
+		if (err < 0) {
 			err = -EIO;
 			goto out;
 		}
diff -uprN a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c
--- a/fs/fat/namei_vfat.c	2012-07-03 07:21:53.758637114 -0500
+++ b/fs/fat/namei_vfat.c	2012-07-03 10:07:58.359967376 -0500
@@ -914,7 +914,7 @@ static int vfat_rename(struct inode *old
 	struct inode *old_inode, *new_inode;
 	struct fat_slot_info old_sinfo, sinfo;
 	struct timespec ts;
-	loff_t dotdot_i_pos, new_i_pos;
+	loff_t new_i_pos;
 	int err, is_dir, update_dotdot, corrupt = 0;
 	struct super_block *sb = old_dir->i_sb;
 
@@ -929,8 +929,8 @@ static int vfat_rename(struct inode *old
 	is_dir = S_ISDIR(old_inode->i_mode);
 	update_dotdot = (is_dir && old_dir != new_dir);
 	if (update_dotdot) {
-		if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de,
-					 &dotdot_i_pos) < 0) {
+		err = fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de);
+		if (err < 0) {
 			err = -EIO;
 			goto out;
 		}

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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ