[PATCH 1/2] fat (exportfs): drop ineffective get_parent code

From: Steven J. Magnani
Date: Tue Jul 03 2012 - 15:10:00 EST


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@xxxxxxxxxxxxxxx>
---
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@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/