All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/2] fat (exportfs): fix dentry reconnection
@ 2012-07-10 14:20 Steven J. Magnani
  2012-07-10 14:20 ` [PATCH 1/2] fat (exportfs): move NFS support code Steven J. Magnani
  2012-07-10 14:20 ` [PATCH 2/2] fat (exportfs): fix dentry reconnection Steven J. Magnani
  0 siblings, 2 replies; 5+ messages in thread
From: Steven J. Magnani @ 2012-07-10 14:20 UTC (permalink / raw)
  To: OGAWA Hirofumi; +Cc: linux-kernel, steve

Under memory pressure, the system may evict dentries from cache.
When the FAT driver receives a NFS request involving an evicted dentry,
it is unable to reconnect it to the filesystem root. 
This causes the request to fail, often with ENOENT.

This is partially due to ineffectiveness of the current FAT NFS implementation,
and partially due to an unimplemented fh_to_parent method. The latter
can cause file accesses to fail on shares exported with subtree_check.

This patch set provides the FAT driver with the ability to reconnect dentries.
NFS file handle generation and lookups are simplified and made congruent with
ext2.

Testing has involved a memory-starved virtual machine running 3.5-rc5
that exports a ~2 GB vfat filesystem containing a kernel tree
(~770 MB, ~40000 files, 9 levels). Both 'cp -r' and 'ls -lR' operations
were performed from a client, some overlapping, some consecutive.
Exports with 'subtree_check' and 'no_subtree_check' have been tested.

Note that while this patch set improves FAT's NFS support, it does not
eliminate ESTALE errors completely.

The following should be considered for NFS clients who are sensitive to ESTALE:

* Mounting with lookupcache=none
  Unfortunately this can degrade performance severely, particularly for deep
  filesystems.

* Incorporating VFS patches to retry ESTALE failures on the client-side, 
  such as https://lkml.org/lkml/2012/6/29/381

* Handling ESTALE errors in client application code


This series depends on the following patches:
* fat: Fix non-atomic NFS i_pos read
* fat: Accessors for msdos_dir_entry 'start' fields
* fat: Refactor shortname parsing

Changes since v1:
* Dropped code to reconstitute evicted inodes. Too risky.
* Dropped FAT-specific get_name method. Not needed.
* Added index of directories by i_logstart (per. H. OGAWA)
  and "nfs" mount option to enable it



^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] fat (exportfs): move NFS support code
  2012-07-10 14:20 [PATCH v2 0/2] fat (exportfs): fix dentry reconnection Steven J. Magnani
@ 2012-07-10 14:20 ` Steven J. Magnani
  2012-07-12 10:02   ` OGAWA Hirofumi
  2012-07-10 14:20 ` [PATCH 2/2] fat (exportfs): fix dentry reconnection Steven J. Magnani
  1 sibling, 1 reply; 5+ messages in thread
From: Steven J. Magnani @ 2012-07-10 14:20 UTC (permalink / raw)
  To: OGAWA Hirofumi; +Cc: linux-kernel, steve

Move NFS-related code into its own C file.
No functional changes.

Signed-off-by: Steven J. Magnani <steve@digidescorp.com>
---
diff -uprN a/fs/fat/fat.h b/fs/fat/fat.h
--- a/fs/fat/fat.h	2012-07-09 08:33:25.206615179 -0500
+++ b/fs/fat/fat.h	2012-07-10 09:10:14.064615822 -0500
@@ -341,6 +341,20 @@ extern int fat_fill_super(struct super_b
 
 extern int fat_flush_inodes(struct super_block *sb, struct inode *i1,
 		            struct inode *i2);
+static inline loff_t fat_i_pos_read(struct msdos_sb_info *sbi,
+				    struct inode *inode)
+{
+	loff_t i_pos;
+#if BITS_PER_LONG == 32
+	spin_lock(&sbi->inode_hash_lock);
+#endif
+	i_pos = MSDOS_I(inode)->i_pos;
+#if BITS_PER_LONG == 32
+	spin_unlock(&sbi->inode_hash_lock);
+#endif
+	return i_pos;
+}
+
 /* fat/misc.c */
 extern __printf(3, 4) __cold
 void __fat_fs_error(struct super_block *sb, int report, const char *fmt, ...);
@@ -366,6 +380,14 @@ extern int fat_sync_bhs(struct buffer_he
 int fat_cache_init(void);
 void fat_cache_destroy(void);
 
+/* fat/nfs.c */
+struct fid;
+extern int fat_encode_fh(struct inode *inode, __u32 *fh, int *lenp,
+			 struct inode *parent);
+extern struct dentry *fat_fh_to_dentry(struct super_block *sb, struct fid *fid,
+				       int fh_len, int fh_type);
+extern struct dentry *fat_get_parent(struct dentry *child_dir);
+
 /* helper for printk */
 typedef unsigned long long	llu;
 
diff -uprN a/fs/fat/inode.c b/fs/fat/inode.c
--- a/fs/fat/inode.c	2012-07-09 08:33:25.249614939 -0500
+++ b/fs/fat/inode.c	2012-07-10 09:09:58.654691584 -0500
@@ -557,20 +557,6 @@ static int fat_statfs(struct dentry *den
 	return 0;
 }
 
-static inline loff_t fat_i_pos_read(struct msdos_sb_info *sbi,
-				    struct inode *inode)
-{
-	loff_t i_pos;
-#if BITS_PER_LONG == 32
-	spin_lock(&sbi->inode_hash_lock);
-#endif
-	i_pos = MSDOS_I(inode)->i_pos;
-#if BITS_PER_LONG == 32
-	spin_unlock(&sbi->inode_hash_lock);
-#endif
-	return i_pos;
-}
-
 static int __fat_write_inode(struct inode *inode, int wait)
 {
 	struct super_block *sb = inode->i_sb;
@@ -663,122 +649,6 @@ static const struct super_operations fat
 	.show_options	= fat_show_options,
 };
 
-/*
- * a FAT file handle with fhtype 3 is
- *  0/  i_ino - for fast, reliable lookup if still in the cache
- *  1/  i_generation - to see if i_ino is still valid
- *          bit 0 == 0 iff directory
- *  2/  i_pos(8-39) - if ino has changed, but still in cache
- *  3/  i_pos(4-7)|i_logstart - to semi-verify inode found at i_pos
- *  4/  i_pos(0-3)|parent->i_logstart - maybe used to hunt for the file on disc
- *
- * Hack for NFSv2: Maximum FAT entry number is 28bits and maximum
- * i_pos is 40bits (blocknr(32) + dir offset(8)), so two 4bits
- * of i_logstart is used to store the directory entry offset.
- */
-
-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;
-
-	if (fh_len < 5 || fh_type != 3)
-		return NULL;
-
-	inode = ilookup(sb, fh[0]);
-	if (!inode || inode->i_generation != fh[1]) {
-		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);
-
-		/* try 2 - see if i_pos is in F-d-c
-		 * require i_logstart to be the same
-		 * Will fail if you truncate and then re-write
-		 */
-
-		inode = fat_iget(sb, i_pos);
-		if (inode && MSDOS_I(inode)->i_logstart != i_logstart) {
-			iput(inode);
-			inode = NULL;
-		}
-	}
-
-	/*
-	 * For now, do nothing if the inode is not found.
-	 *
-	 * What we could do is:
-	 *
-	 *	- follow the file starting at fh[4], and record the ".." entry,
-	 *	  and the name of the fh[2] entry.
-	 *	- then follow the ".." file finding the next step up.
-	 *
-	 * This way we build a path to the root of the tree. If this works, we
-	 * lookup the path and so get this inode into the cache.  Finally try
-	 * the fat_iget lookup again.  If that fails, then we are totally out
-	 * of luck.  But all that is for another day
-	 */
-	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)
-{
-	struct super_block *sb = child->d_sb;
-	struct buffer_head *bh;
-	struct msdos_dir_entry *de;
-	loff_t i_pos;
-	struct dentry *parent;
-	struct inode *inode;
-	int err;
-
-	lock_super(sb);
-
-	err = fat_get_dotdot_entry(child->d_inode, &bh, &de, &i_pos);
-	if (err) {
-		parent = ERR_PTR(err);
-		goto out;
-	}
-	inode = fat_build_inode(sb, de, i_pos);
-	brelse(bh);
-
-	parent = d_obtain_alias(inode);
-out:
-	unlock_super(sb);
-
-	return parent;
-}
-
 static const struct export_operations fat_export_ops = {
 	.encode_fh	= fat_encode_fh,
 	.fh_to_dentry	= fat_fh_to_dentry,
diff -uprN a/fs/fat/Makefile b/fs/fat/Makefile
--- a/fs/fat/Makefile	2012-07-09 08:33:25.240614990 -0500
+++ b/fs/fat/Makefile	2012-07-09 08:33:25.370614268 -0500
@@ -6,6 +6,6 @@ obj-$(CONFIG_FAT_FS) += fat.o
 obj-$(CONFIG_VFAT_FS) += vfat.o
 obj-$(CONFIG_MSDOS_FS) += msdos.o
 
-fat-y := cache.o dir.o fatent.o file.o inode.o misc.o
+fat-y := cache.o dir.o fatent.o file.o inode.o misc.o nfs.o
 vfat-y := namei_vfat.o
 msdos-y := namei_msdos.o
diff -uprN a/fs/fat/nfs.c b/fs/fat/nfs.c
--- a/fs/fat/nfs.c	1969-12-31 18:00:00.000000000 -0600
+++ b/fs/fat/nfs.c	2012-07-10 08:53:24.272703129 -0500
@@ -0,0 +1,151 @@
+/* fs/fat/nfs.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/exportfs.h>
+#include "fat.h"
+
+/*
+ * a FAT file handle with fhtype 3 is
+ *  0/  i_ino - for fast, reliable lookup if still in the cache
+ *  1/  i_generation - to see if i_ino is still valid
+ *          bit 0 == 0 iff directory
+ *  2/  i_pos(8-39) - if ino has changed, but still in cache
+ *  3/  i_pos(4-7)|i_logstart - to semi-verify inode found at i_pos
+ *  4/  i_pos(0-3)|parent->i_logstart - maybe used to hunt for the file on disc
+ *
+ * Hack for NFSv2: Maximum FAT entry number is 28bits and maximum
+ * i_pos is 40bits (blocknr(32) + dir offset(8)), so two 4bits
+ * of i_logstart is used to store the directory entry offset.
+ */
+
+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));
+}
+
+/**
+ * Map a NFS file handle to a corresponding dentry.
+ * The dentry may or may not be connected to the filesystem root.
+ */
+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 (!fat_is_valid_fh(fh_len, fh_type))
+		return NULL;
+
+	i_ino = fh[0];
+	i_generation = fh[1];
+	i_logstart = fh[3] & 0x0fffffff;
+
+	/* Try i_ino lookup first - fastest and most reliable */
+	inode = ilookup(sb, i_ino);
+	if (inode && (inode->i_generation != i_generation)) {
+		iput(inode);
+		inode = NULL;
+	}
+	if (!inode) {
+		i_pos = (loff_t)fh[2] << 8;
+		i_pos |= ((fh[3] >> 24) & 0xf0) | (fh[4] >> 28);
+
+		/* try 2 - see if i_pos is in F-d-c
+		 * require i_logstart to be the same
+		 * Will fail if you truncate and then re-write
+		 */
+
+		inode = fat_iget(sb, i_pos);
+		if (inode && MSDOS_I(inode)->i_logstart != i_logstart) {
+			iput(inode);
+			inode = NULL;
+		}
+	}
+
+	/*
+	 * For now, do nothing if the inode is not found.
+	 *
+	 * What we could do is:
+	 *
+	 *	- follow the file starting at fh[4], and record the ".." entry,
+	 *	  and the name of the fh[2] entry.
+	 *	- then follow the ".." file finding the next step up.
+	 *
+	 * This way we build a path to the root of the tree. If this works, we
+	 * lookup the path and so get this inode into the cache.  Finally try
+	 * the fat_iget lookup again.  If that fails, then we are totally out
+	 * of luck.  But all that is for another day
+	 */
+	return d_obtain_alias(inode);
+}
+
+/*
+ * Find the parent for a directory that is not currently connected to
+ * the filesystem root.
+ *
+ * On entry, the caller holds child_dir->d_inode->i_mutex.
+ */
+struct dentry *fat_get_parent(struct dentry *child_dir)
+{
+	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;
+
+	lock_super(sb);
+
+	err = fat_get_dotdot_entry(child_dir->d_inode, &bh, &de, &i_pos);
+	if (err) {
+		parent = ERR_PTR(err);
+		goto out;
+	}
+	inode = fat_build_inode(sb, de, i_pos);
+
+	parent = d_obtain_alias(inode);
+out:
+	brelse(bh);
+	unlock_super(sb);
+
+	return parent;
+}


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 2/2] fat (exportfs): fix dentry reconnection
  2012-07-10 14:20 [PATCH v2 0/2] fat (exportfs): fix dentry reconnection Steven J. Magnani
  2012-07-10 14:20 ` [PATCH 1/2] fat (exportfs): move NFS support code Steven J. Magnani
@ 2012-07-10 14:20 ` Steven J. Magnani
  2012-07-12 10:12   ` OGAWA Hirofumi
  1 sibling, 1 reply; 5+ messages in thread
From: Steven J. Magnani @ 2012-07-10 14:20 UTC (permalink / raw)
  To: OGAWA Hirofumi; +Cc: linux-kernel, steve

Maintain an index of directory inodes by starting cluster,
so that fat_get_parent() can return the proper cached inode rather than
inventing one that cannot be traced back to the filesystem root.

Add a new msdos/vfat binary mount option "nfs" so that FAT filesystems
that are _not_ exported via NFS are not saddled with maintenance of an index
they will never use.

Finally, simplify NFS file handle generation and lookups. 
An ext2-congruent implementation is adequate for FAT needs.

Signed-off-by: Steven J. Magnani <steve@digidescorp.com>
---
diff -uprN a/fs/fat/dir.c b/fs/fat/dir.c
--- a/fs/fat/dir.c	2012-07-10 06:38:35.986886625 -0500
+++ b/fs/fat/dir.c	2012-07-09 08:46:10.710590455 -0500
@@ -872,21 +872,23 @@ static int fat_get_short_entry(struct in
 }
 
 /*
- * The ".." entry can not provide the "struct fat_slot_info" informations
- * for inode. So, this function provide the some informations only.
+ * The ".." entry can not provide the "struct fat_slot_info" information
+ * for inode, nor a usable i_pos. So, this function provides some information
+ * only.
+ *
+ * Since this function walks through the on-disk inodes within a directory,
+ * callers are responsible for taking any locks necessary to prevent the
+ * directory from changing.
  */
 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-10 06:38:00.785032283 -0500
+++ b/fs/fat/fat.h	2012-07-10 09:16:33.777921036 -0500
@@ -5,6 +5,7 @@
 #include <linux/string.h>
 #include <linux/nls.h>
 #include <linux/fs.h>
+#include <linux/hash.h>
 #include <linux/mutex.h>
 #include <linux/ratelimit.h>
 #include <linux/msdos_fs.h>
@@ -46,7 +47,8 @@ struct fat_mount_options {
 		 usefree:1,	  /* Use free_clusters for FAT32 */
 		 tz_utc:1,	  /* Filesystem timestamps are in UTC */
 		 rodir:1,	  /* allow ATTR_RO for directory */
-		 discard:1;	  /* Issue discard requests on deletions */
+		 discard:1,	  /* Issue discard requests on deletions */
+		 nfs:1;		  /* Do extra work needed for NFS export */
 };
 
 #define FAT_HASH_BITS	8
@@ -88,6 +90,9 @@ struct msdos_sb_info {
 
 	spinlock_t inode_hash_lock;
 	struct hlist_head inode_hashtable[FAT_HASH_SIZE];
+
+	spinlock_t dir_hash_lock;
+	struct hlist_head dir_hashtable[FAT_HASH_SIZE];
 };
 
 #define FAT_CACHE_VALID	0	/* special case for valid cache */
@@ -110,6 +115,7 @@ struct msdos_inode_info {
 	int i_attrs;		/* unused attribute bits */
 	loff_t i_pos;		/* on-disk position of directory entry or 0 */
 	struct hlist_node i_fat_hash;	/* hash by i_location */
+	struct hlist_node i_dir_hash;	/* hash by i_logstart */
 	struct rw_semaphore truncate_lock; /* protect bmap against truncate */
 	struct inode vfs_inode;
 };
@@ -262,7 +268,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);
@@ -341,18 +347,9 @@ extern int fat_fill_super(struct super_b
 
 extern int fat_flush_inodes(struct super_block *sb, struct inode *i1,
 		            struct inode *i2);
-static inline loff_t fat_i_pos_read(struct msdos_sb_info *sbi,
-				    struct inode *inode)
+static inline unsigned long fat_dir_hash(int logstart)
 {
-	loff_t i_pos;
-#if BITS_PER_LONG == 32
-	spin_lock(&sbi->inode_hash_lock);
-#endif
-	i_pos = MSDOS_I(inode)->i_pos;
-#if BITS_PER_LONG == 32
-	spin_unlock(&sbi->inode_hash_lock);
-#endif
-	return i_pos;
+	return hash_32(logstart, FAT_HASH_BITS);
 }
 
 /* fat/misc.c */
@@ -382,10 +379,10 @@ void fat_cache_destroy(void);
 
 /* fat/nfs.c */
 struct fid;
-extern int fat_encode_fh(struct inode *inode, __u32 *fh, int *lenp,
-			 struct inode *parent);
 extern struct dentry *fat_fh_to_dentry(struct super_block *sb, struct fid *fid,
 				       int fh_len, int fh_type);
+extern struct dentry *fat_fh_to_parent(struct super_block *sb, struct fid *fid,
+				       int fh_len, int fh_type);
 extern struct dentry *fat_get_parent(struct dentry *child_dir);
 
 /* helper for printk */
diff -uprN a/fs/fat/inode.c b/fs/fat/inode.c
--- a/fs/fat/inode.c	2012-07-10 06:38:21.809948291 -0500
+++ b/fs/fat/inode.c	2012-07-10 09:17:30.714658962 -0500
@@ -281,15 +281,42 @@ static inline unsigned long fat_hash(lof
 	return hash_32(i_pos, FAT_HASH_BITS);
 }
 
+static void dir_hash_init(struct super_block *sb)
+{
+	struct msdos_sb_info *sbi = MSDOS_SB(sb);
+	int i;
+
+	spin_lock_init(&sbi->dir_hash_lock);
+	for (i = 0; i < FAT_HASH_SIZE; i++)
+		INIT_HLIST_HEAD(&sbi->dir_hashtable[i]);
+}
+
 void fat_attach(struct inode *inode, loff_t i_pos)
 {
 	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
-	struct hlist_head *head = sbi->inode_hashtable + fat_hash(i_pos);
 
-	spin_lock(&sbi->inode_hash_lock);
-	MSDOS_I(inode)->i_pos = i_pos;
-	hlist_add_head(&MSDOS_I(inode)->i_fat_hash, head);
-	spin_unlock(&sbi->inode_hash_lock);
+	if (inode->i_ino != MSDOS_ROOT_INO) {
+		struct hlist_head *head =   sbi->inode_hashtable
+					  + fat_hash(i_pos);
+
+		spin_lock(&sbi->inode_hash_lock);
+		MSDOS_I(inode)->i_pos = i_pos;
+		hlist_add_head(&MSDOS_I(inode)->i_fat_hash, head);
+		spin_unlock(&sbi->inode_hash_lock);
+	}
+
+	/* If NFS support is enabled, cache the mapping of start cluster
+	 * to directory inode. This is used during reconnection of
+	 * dentries to the filesystem root.
+	 */
+	if (S_ISDIR(inode->i_mode) && sbi->options.nfs) {
+		struct hlist_head *d_head = sbi->dir_hashtable;
+		d_head += fat_dir_hash(MSDOS_I(inode)->i_logstart);
+
+		spin_lock(&sbi->dir_hash_lock);
+		hlist_add_head(&MSDOS_I(inode)->i_dir_hash, d_head);
+		spin_unlock(&sbi->dir_hash_lock);
+	}
 }
 EXPORT_SYMBOL_GPL(fat_attach);
 
@@ -300,6 +327,12 @@ void fat_detach(struct inode *inode)
 	MSDOS_I(inode)->i_pos = 0;
 	hlist_del_init(&MSDOS_I(inode)->i_fat_hash);
 	spin_unlock(&sbi->inode_hash_lock);
+
+	if (S_ISDIR(inode->i_mode) && sbi->options.nfs) {
+		spin_lock(&sbi->dir_hash_lock);
+		hlist_del_init(&MSDOS_I(inode)->i_dir_hash);
+		spin_unlock(&sbi->dir_hash_lock);
+	}
 }
 EXPORT_SYMBOL_GPL(fat_detach);
 
@@ -504,6 +537,7 @@ static void init_once(void *foo)
 	ei->cache_valid_id = FAT_CACHE_VALID + 1;
 	INIT_LIST_HEAD(&ei->cache_lru);
 	INIT_HLIST_NODE(&ei->i_fat_hash);
+	INIT_HLIST_NODE(&ei->i_dir_hash);
 	inode_init_once(&ei->vfs_inode);
 }
 
@@ -557,6 +591,20 @@ static int fat_statfs(struct dentry *den
 	return 0;
 }
 
+static inline loff_t fat_i_pos_read(struct msdos_sb_info *sbi,
+				    struct inode *inode)
+{
+	loff_t i_pos;
+#if BITS_PER_LONG == 32
+	spin_lock(&sbi->inode_hash_lock);
+#endif
+	i_pos = MSDOS_I(inode)->i_pos;
+#if BITS_PER_LONG == 32
+	spin_unlock(&sbi->inode_hash_lock);
+#endif
+	return i_pos;
+}
+
 static int __fat_write_inode(struct inode *inode, int wait)
 {
 	struct super_block *sb = inode->i_sb;
@@ -650,8 +698,8 @@ static const struct super_operations fat
 };
 
 static const struct export_operations fat_export_ops = {
-	.encode_fh	= fat_encode_fh,
 	.fh_to_dentry	= fat_fh_to_dentry,
+	.fh_to_parent	= fat_fh_to_parent,
 	.get_parent	= fat_get_parent,
 };
 
@@ -699,6 +747,8 @@ static int fat_show_options(struct seq_f
 		seq_puts(m, ",usefree");
 	if (opts->quiet)
 		seq_puts(m, ",quiet");
+	if (opts->nfs)
+		seq_puts(m, ",nfs");
 	if (opts->showexec)
 		seq_puts(m, ",showexec");
 	if (opts->sys_immutable)
@@ -743,7 +793,7 @@ enum {
 	Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes,
 	Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes,
 	Opt_obsolete, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err_cont,
-	Opt_err_panic, Opt_err_ro, Opt_discard, Opt_err,
+	Opt_err_panic, Opt_err_ro, Opt_discard, Opt_nfs, Opt_err,
 };
 
 static const match_table_t fat_tokens = {
@@ -772,6 +822,7 @@ static const match_table_t fat_tokens =
 	{Opt_err_panic, "errors=panic"},
 	{Opt_err_ro, "errors=remount-ro"},
 	{Opt_discard, "discard"},
+	{Opt_nfs, "nfs"},
 	{Opt_obsolete, "conv=binary"},
 	{Opt_obsolete, "conv=text"},
 	{Opt_obsolete, "conv=auto"},
@@ -852,6 +903,7 @@ static int parse_options(struct super_bl
 	opts->numtail = 1;
 	opts->usefree = opts->nocase = 0;
 	opts->tz_utc = 0;
+	opts->nfs = 0;
 	opts->errors = FAT_ERRORS_RO;
 	*debug = 0;
 
@@ -1012,6 +1064,9 @@ static int parse_options(struct super_bl
 		case Opt_discard:
 			opts->discard = 1;
 			break;
+		case Opt_nfs:
+			opts->nfs = 1;
+			break;
 
 		/* obsolete mount options */
 		case Opt_obsolete:
@@ -1302,6 +1357,7 @@ int fat_fill_super(struct super_block *s
 
 	/* set up enough so that it can read an inode */
 	fat_hash_init(sb);
+	dir_hash_init(sb);
 	fat_ent_access_init(sb);
 
 	/*
@@ -1356,6 +1412,7 @@ int fat_fill_super(struct super_block *s
 	}
 	error = -ENOMEM;
 	insert_inode_hash(root_inode);
+	fat_attach(root_inode, 0);
 	sb->s_root = d_make_root(root_inode);
 	if (!sb->s_root) {
 		fat_msg(sb, KERN_ERR, "get root inode failed");
diff -uprN a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c
--- a/fs/fat/namei_msdos.c	2012-07-09 08:33:25.395614128 -0500
+++ b/fs/fat/namei_msdos.c	2012-07-09 08:53:00.962540730 -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,7 @@ 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) {
+		if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de)) {
 			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-09 08:33:25.350614379 -0500
+++ b/fs/fat/namei_vfat.c	2012-07-09 08:53:18.406454301 -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,7 @@ 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) {
+		if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de)) {
 			err = -EIO;
 			goto out;
 		}
diff -uprN a/fs/fat/nfs.c b/fs/fat/nfs.c
--- a/fs/fat/nfs.c	2012-07-09 08:33:25.321614540 -0500
+++ b/fs/fat/nfs.c	2012-07-10 07:59:16.344498452 -0500
@@ -14,47 +14,46 @@
 #include <linux/exportfs.h>
 #include "fat.h"
 
-/*
- * a FAT file handle with fhtype 3 is
- *  0/  i_ino - for fast, reliable lookup if still in the cache
- *  1/  i_generation - to see if i_ino is still valid
- *          bit 0 == 0 iff directory
- *  2/  i_pos(8-39) - if ino has changed, but still in cache
- *  3/  i_pos(4-7)|i_logstart - to semi-verify inode found at i_pos
- *  4/  i_pos(0-3)|parent->i_logstart - maybe used to hunt for the file on disc
- *
- * Hack for NFSv2: Maximum FAT entry number is 28bits and maximum
- * i_pos is 40bits (blocknr(32) + dir offset(8)), so two 4bits
- * of i_logstart is used to store the directory entry offset.
+/**
+ * Look up a directory inode given its starting cluster.
  */
-
-int
-fat_encode_fh(struct inode *inode, __u32 *fh, int *lenp, struct inode *parent)
+static struct inode *fat_dget(struct super_block *sb, int i_logstart)
 {
-	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 */
-	}
+	struct msdos_sb_info *sbi = MSDOS_SB(sb);
+	struct hlist_head *head;
+	struct hlist_node *_p;
+	struct msdos_inode_info *i;
+	struct inode *inode = NULL;
 
-	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;
+	head = sbi->dir_hashtable + fat_dir_hash(i_logstart);
+	spin_lock(&sbi->dir_hash_lock);
+	hlist_for_each_entry(i, _p, head, i_dir_hash) {
+		BUG_ON(i->vfs_inode.i_sb != sb);
+		if (i->i_logstart != i_logstart)
+			continue;
+		inode = igrab(&i->vfs_inode);
+		if (inode)
+			break;
+	}
+	spin_unlock(&sbi->dir_hash_lock);
+	return inode;
 }
 
-static int fat_is_valid_fh(int fh_len, int fh_type)
+static struct inode *fat_nfs_get_inode(struct super_block *sb,
+				       u64 ino, u32 generation)
 {
-	return ((fh_len >= 5) && (fh_type == 3));
+	struct inode *inode;
+
+	if ((ino < MSDOS_ROOT_INO) || (ino == MSDOS_FSINFO_INO))
+		return NULL;
+
+	inode = ilookup(sb, ino);
+	if (inode && generation && (inode->i_generation != generation)) {
+		iput(inode);
+		inode = NULL;
+	}
+
+	return inode;
 }
 
 /**
@@ -64,57 +63,19 @@ static int fat_is_valid_fh(int fh_len, i
 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 (!fat_is_valid_fh(fh_len, fh_type))
-		return NULL;
-
-	i_ino = fh[0];
-	i_generation = fh[1];
-	i_logstart = fh[3] & 0x0fffffff;
-
-	/* Try i_ino lookup first - fastest and most reliable */
-	inode = ilookup(sb, i_ino);
-	if (inode && (inode->i_generation != i_generation)) {
-		iput(inode);
-		inode = NULL;
-	}
-	if (!inode) {
-		i_pos = (loff_t)fh[2] << 8;
-		i_pos |= ((fh[3] >> 24) & 0xf0) | (fh[4] >> 28);
-
-		/* try 2 - see if i_pos is in F-d-c
-		 * require i_logstart to be the same
-		 * Will fail if you truncate and then re-write
-		 */
-
-		inode = fat_iget(sb, i_pos);
-		if (inode && MSDOS_I(inode)->i_logstart != i_logstart) {
-			iput(inode);
-			inode = NULL;
-		}
-	}
+	return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
+				    fat_nfs_get_inode);
+}
 
-	/*
-	 * For now, do nothing if the inode is not found.
-	 *
-	 * What we could do is:
-	 *
-	 *	- follow the file starting at fh[4], and record the ".." entry,
-	 *	  and the name of the fh[2] entry.
-	 *	- then follow the ".." file finding the next step up.
-	 *
-	 * This way we build a path to the root of the tree. If this works, we
-	 * lookup the path and so get this inode into the cache.  Finally try
-	 * the fat_iget lookup again.  If that fails, then we are totally out
-	 * of luck.  But all that is for another day
-	 */
-	return d_obtain_alias(inode);
+/*
+ * Find the parent for a file specified by NFS handle.
+ * This requires that the handle contain the i_ino of the parent.
+ */
+struct dentry *fat_fh_to_parent(struct super_block *sb, struct fid *fid,
+				int fh_len, int fh_type)
+{
+	return generic_fh_to_parent(sb, fid, fh_len, fh_type,
+				    fat_nfs_get_inode);
 }
 
 /*
@@ -128,24 +89,13 @@ struct dentry *fat_get_parent(struct den
 	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;
-
-	lock_super(sb);
+	struct inode *parent_inode = NULL;
 
-	err = fat_get_dotdot_entry(child_dir->d_inode, &bh, &de, &i_pos);
-	if (err) {
-		parent = ERR_PTR(err);
-		goto out;
+	if (!fat_get_dotdot_entry(child_dir->d_inode, &bh, &de)) {
+		int parent_logstart = fat_get_start(MSDOS_SB(sb), de);
+		parent_inode = fat_dget(sb, parent_logstart);
 	}
-	inode = fat_build_inode(sb, de, i_pos);
-
-	parent = d_obtain_alias(inode);
-out:
 	brelse(bh);
-	unlock_super(sb);
 
-	return parent;
+	return d_obtain_alias(parent_inode);
 }


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] fat (exportfs): move NFS support code
  2012-07-10 14:20 ` [PATCH 1/2] fat (exportfs): move NFS support code Steven J. Magnani
@ 2012-07-12 10:02   ` OGAWA Hirofumi
  0 siblings, 0 replies; 5+ messages in thread
From: OGAWA Hirofumi @ 2012-07-12 10:02 UTC (permalink / raw)
  To: Steven J. Magnani; +Cc: linux-kernel

"Steven J. Magnani" <steve@digidescorp.com> writes:

> Move NFS-related code into its own C file.
> No functional changes.

Looks good.

Acked-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>

> Signed-off-by: Steven J. Magnani <steve@digidescorp.com>
> ---
> diff -uprN a/fs/fat/fat.h b/fs/fat/fat.h
> --- a/fs/fat/fat.h	2012-07-09 08:33:25.206615179 -0500
> +++ b/fs/fat/fat.h	2012-07-10 09:10:14.064615822 -0500
> @@ -341,6 +341,20 @@ extern int fat_fill_super(struct super_b
>  
>  extern int fat_flush_inodes(struct super_block *sb, struct inode *i1,
>  		            struct inode *i2);
> +static inline loff_t fat_i_pos_read(struct msdos_sb_info *sbi,
> +				    struct inode *inode)
> +{
> +	loff_t i_pos;
> +#if BITS_PER_LONG == 32
> +	spin_lock(&sbi->inode_hash_lock);
> +#endif
> +	i_pos = MSDOS_I(inode)->i_pos;
> +#if BITS_PER_LONG == 32
> +	spin_unlock(&sbi->inode_hash_lock);
> +#endif
> +	return i_pos;
> +}
> +
>  /* fat/misc.c */
>  extern __printf(3, 4) __cold
>  void __fat_fs_error(struct super_block *sb, int report, const char *fmt, ...);
> @@ -366,6 +380,14 @@ extern int fat_sync_bhs(struct buffer_he
>  int fat_cache_init(void);
>  void fat_cache_destroy(void);
>  
> +/* fat/nfs.c */
> +struct fid;
> +extern int fat_encode_fh(struct inode *inode, __u32 *fh, int *lenp,
> +			 struct inode *parent);
> +extern struct dentry *fat_fh_to_dentry(struct super_block *sb, struct fid *fid,
> +				       int fh_len, int fh_type);
> +extern struct dentry *fat_get_parent(struct dentry *child_dir);
> +
>  /* helper for printk */
>  typedef unsigned long long	llu;
>  
> diff -uprN a/fs/fat/inode.c b/fs/fat/inode.c
> --- a/fs/fat/inode.c	2012-07-09 08:33:25.249614939 -0500
> +++ b/fs/fat/inode.c	2012-07-10 09:09:58.654691584 -0500
> @@ -557,20 +557,6 @@ static int fat_statfs(struct dentry *den
>  	return 0;
>  }
>  
> -static inline loff_t fat_i_pos_read(struct msdos_sb_info *sbi,
> -				    struct inode *inode)
> -{
> -	loff_t i_pos;
> -#if BITS_PER_LONG == 32
> -	spin_lock(&sbi->inode_hash_lock);
> -#endif
> -	i_pos = MSDOS_I(inode)->i_pos;
> -#if BITS_PER_LONG == 32
> -	spin_unlock(&sbi->inode_hash_lock);
> -#endif
> -	return i_pos;
> -}
> -
>  static int __fat_write_inode(struct inode *inode, int wait)
>  {
>  	struct super_block *sb = inode->i_sb;
> @@ -663,122 +649,6 @@ static const struct super_operations fat
>  	.show_options	= fat_show_options,
>  };
>  
> -/*
> - * a FAT file handle with fhtype 3 is
> - *  0/  i_ino - for fast, reliable lookup if still in the cache
> - *  1/  i_generation - to see if i_ino is still valid
> - *          bit 0 == 0 iff directory
> - *  2/  i_pos(8-39) - if ino has changed, but still in cache
> - *  3/  i_pos(4-7)|i_logstart - to semi-verify inode found at i_pos
> - *  4/  i_pos(0-3)|parent->i_logstart - maybe used to hunt for the file on disc
> - *
> - * Hack for NFSv2: Maximum FAT entry number is 28bits and maximum
> - * i_pos is 40bits (blocknr(32) + dir offset(8)), so two 4bits
> - * of i_logstart is used to store the directory entry offset.
> - */
> -
> -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;
> -
> -	if (fh_len < 5 || fh_type != 3)
> -		return NULL;
> -
> -	inode = ilookup(sb, fh[0]);
> -	if (!inode || inode->i_generation != fh[1]) {
> -		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);
> -
> -		/* try 2 - see if i_pos is in F-d-c
> -		 * require i_logstart to be the same
> -		 * Will fail if you truncate and then re-write
> -		 */
> -
> -		inode = fat_iget(sb, i_pos);
> -		if (inode && MSDOS_I(inode)->i_logstart != i_logstart) {
> -			iput(inode);
> -			inode = NULL;
> -		}
> -	}
> -
> -	/*
> -	 * For now, do nothing if the inode is not found.
> -	 *
> -	 * What we could do is:
> -	 *
> -	 *	- follow the file starting at fh[4], and record the ".." entry,
> -	 *	  and the name of the fh[2] entry.
> -	 *	- then follow the ".." file finding the next step up.
> -	 *
> -	 * This way we build a path to the root of the tree. If this works, we
> -	 * lookup the path and so get this inode into the cache.  Finally try
> -	 * the fat_iget lookup again.  If that fails, then we are totally out
> -	 * of luck.  But all that is for another day
> -	 */
> -	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)
> -{
> -	struct super_block *sb = child->d_sb;
> -	struct buffer_head *bh;
> -	struct msdos_dir_entry *de;
> -	loff_t i_pos;
> -	struct dentry *parent;
> -	struct inode *inode;
> -	int err;
> -
> -	lock_super(sb);
> -
> -	err = fat_get_dotdot_entry(child->d_inode, &bh, &de, &i_pos);
> -	if (err) {
> -		parent = ERR_PTR(err);
> -		goto out;
> -	}
> -	inode = fat_build_inode(sb, de, i_pos);
> -	brelse(bh);
> -
> -	parent = d_obtain_alias(inode);
> -out:
> -	unlock_super(sb);
> -
> -	return parent;
> -}
> -
>  static const struct export_operations fat_export_ops = {
>  	.encode_fh	= fat_encode_fh,
>  	.fh_to_dentry	= fat_fh_to_dentry,
> diff -uprN a/fs/fat/Makefile b/fs/fat/Makefile
> --- a/fs/fat/Makefile	2012-07-09 08:33:25.240614990 -0500
> +++ b/fs/fat/Makefile	2012-07-09 08:33:25.370614268 -0500
> @@ -6,6 +6,6 @@ obj-$(CONFIG_FAT_FS) += fat.o
>  obj-$(CONFIG_VFAT_FS) += vfat.o
>  obj-$(CONFIG_MSDOS_FS) += msdos.o
>  
> -fat-y := cache.o dir.o fatent.o file.o inode.o misc.o
> +fat-y := cache.o dir.o fatent.o file.o inode.o misc.o nfs.o
>  vfat-y := namei_vfat.o
>  msdos-y := namei_msdos.o
> diff -uprN a/fs/fat/nfs.c b/fs/fat/nfs.c
> --- a/fs/fat/nfs.c	1969-12-31 18:00:00.000000000 -0600
> +++ b/fs/fat/nfs.c	2012-07-10 08:53:24.272703129 -0500
> @@ -0,0 +1,151 @@
> +/* fs/fat/nfs.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/exportfs.h>
> +#include "fat.h"
> +
> +/*
> + * a FAT file handle with fhtype 3 is
> + *  0/  i_ino - for fast, reliable lookup if still in the cache
> + *  1/  i_generation - to see if i_ino is still valid
> + *          bit 0 == 0 iff directory
> + *  2/  i_pos(8-39) - if ino has changed, but still in cache
> + *  3/  i_pos(4-7)|i_logstart - to semi-verify inode found at i_pos
> + *  4/  i_pos(0-3)|parent->i_logstart - maybe used to hunt for the file on disc
> + *
> + * Hack for NFSv2: Maximum FAT entry number is 28bits and maximum
> + * i_pos is 40bits (blocknr(32) + dir offset(8)), so two 4bits
> + * of i_logstart is used to store the directory entry offset.
> + */
> +
> +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));
> +}
> +
> +/**
> + * Map a NFS file handle to a corresponding dentry.
> + * The dentry may or may not be connected to the filesystem root.
> + */
> +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 (!fat_is_valid_fh(fh_len, fh_type))
> +		return NULL;
> +
> +	i_ino = fh[0];
> +	i_generation = fh[1];
> +	i_logstart = fh[3] & 0x0fffffff;
> +
> +	/* Try i_ino lookup first - fastest and most reliable */
> +	inode = ilookup(sb, i_ino);
> +	if (inode && (inode->i_generation != i_generation)) {
> +		iput(inode);
> +		inode = NULL;
> +	}
> +	if (!inode) {
> +		i_pos = (loff_t)fh[2] << 8;
> +		i_pos |= ((fh[3] >> 24) & 0xf0) | (fh[4] >> 28);
> +
> +		/* try 2 - see if i_pos is in F-d-c
> +		 * require i_logstart to be the same
> +		 * Will fail if you truncate and then re-write
> +		 */
> +
> +		inode = fat_iget(sb, i_pos);
> +		if (inode && MSDOS_I(inode)->i_logstart != i_logstart) {
> +			iput(inode);
> +			inode = NULL;
> +		}
> +	}
> +
> +	/*
> +	 * For now, do nothing if the inode is not found.
> +	 *
> +	 * What we could do is:
> +	 *
> +	 *	- follow the file starting at fh[4], and record the ".." entry,
> +	 *	  and the name of the fh[2] entry.
> +	 *	- then follow the ".." file finding the next step up.
> +	 *
> +	 * This way we build a path to the root of the tree. If this works, we
> +	 * lookup the path and so get this inode into the cache.  Finally try
> +	 * the fat_iget lookup again.  If that fails, then we are totally out
> +	 * of luck.  But all that is for another day
> +	 */
> +	return d_obtain_alias(inode);
> +}
> +
> +/*
> + * Find the parent for a directory that is not currently connected to
> + * the filesystem root.
> + *
> + * On entry, the caller holds child_dir->d_inode->i_mutex.
> + */
> +struct dentry *fat_get_parent(struct dentry *child_dir)
> +{
> +	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;
> +
> +	lock_super(sb);
> +
> +	err = fat_get_dotdot_entry(child_dir->d_inode, &bh, &de, &i_pos);
> +	if (err) {
> +		parent = ERR_PTR(err);
> +		goto out;
> +	}
> +	inode = fat_build_inode(sb, de, i_pos);
> +
> +	parent = d_obtain_alias(inode);
> +out:
> +	brelse(bh);
> +	unlock_super(sb);
> +
> +	return parent;
> +}
>

-- 
OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] fat (exportfs): fix dentry reconnection
  2012-07-10 14:20 ` [PATCH 2/2] fat (exportfs): fix dentry reconnection Steven J. Magnani
@ 2012-07-12 10:12   ` OGAWA Hirofumi
  0 siblings, 0 replies; 5+ messages in thread
From: OGAWA Hirofumi @ 2012-07-12 10:12 UTC (permalink / raw)
  To: Steven J. Magnani; +Cc: linux-kernel

"Steven J. Magnani" <steve@digidescorp.com> writes:

> Maintain an index of directory inodes by starting cluster,
> so that fat_get_parent() can return the proper cached inode rather than
> inventing one that cannot be traced back to the filesystem root.
>
> Add a new msdos/vfat binary mount option "nfs" so that FAT filesystems
> that are _not_ exported via NFS are not saddled with maintenance of an index
> they will never use.
>
> Finally, simplify NFS file handle generation and lookups. 
> An ext2-congruent implementation is adequate for FAT needs.

Looks good to me. Let's test with this.

Acked-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>

> +static struct inode *fat_dget(struct super_block *sb, int i_logstart)
>  {

[...]

If options.nfs == 0, it may be better to warn once.

printk_once()

> +	head = sbi->dir_hashtable + fat_dir_hash(i_logstart);
> +	spin_lock(&sbi->dir_hash_lock);
> +	hlist_for_each_entry(i, _p, head, i_dir_hash) {
> +		BUG_ON(i->vfs_inode.i_sb != sb);
> +		if (i->i_logstart != i_logstart)
> +			continue;
> +		inode = igrab(&i->vfs_inode);
> +		if (inode)
> +			break;
> +	}
> +	spin_unlock(&sbi->dir_hash_lock);
> +	return inode;
>  }

[...]

> @@ -128,24 +89,13 @@ struct dentry *fat_get_parent(struct den
>  	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;
> -
> -	lock_super(sb);
> +	struct inode *parent_inode = NULL;
>  
> -	err = fat_get_dotdot_entry(child_dir->d_inode, &bh, &de, &i_pos);
> -	if (err) {
> -		parent = ERR_PTR(err);
> -		goto out;
> +	if (!fat_get_dotdot_entry(child_dir->d_inode, &bh, &de)) {
> +		int parent_logstart = fat_get_start(MSDOS_SB(sb), de);
> +		parent_inode = fat_dget(sb, parent_logstart);
>  	}

BTW, I guess, in future, if it is improve, we might be able to create
that "private" inode here. And register it to i_dir_hash without i_pos,
and detach while using by ->get_parent(). And fat_attach() side can find
and grab it, and update i_pos by real one.

Thanks.
-- 
OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2012-07-12 10:12 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-07-10 14:20 [PATCH v2 0/2] fat (exportfs): fix dentry reconnection Steven J. Magnani
2012-07-10 14:20 ` [PATCH 1/2] fat (exportfs): move NFS support code Steven J. Magnani
2012-07-12 10:02   ` OGAWA Hirofumi
2012-07-10 14:20 ` [PATCH 2/2] fat (exportfs): fix dentry reconnection Steven J. Magnani
2012-07-12 10:12   ` OGAWA Hirofumi

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.