]> Pileus Git - ~andy/linux/blobdiff - fs/fat/nfs.c
fat: restructure export_operations
[~andy/linux] / fs / fat / nfs.c
index 499c10438ca2f5e53def451d3485cec1963ab657..d59c02543a10ad9433924b41f680d78efdf06f15 100644 (file)
 #include <linux/exportfs.h>
 #include "fat.h"
 
+struct fat_fid {
+       u32 i_gen;
+       u32 i_pos_low;
+       u16 i_pos_hi;
+       u16 parent_i_pos_hi;
+       u32 parent_i_pos_low;
+       u32 parent_i_gen;
+};
+
+#define FAT_FID_SIZE_WITHOUT_PARENT 3
+#define FAT_FID_SIZE_WITH_PARENT (sizeof(struct fat_fid)/sizeof(u32))
+
 /**
  * Look up a directory inode given its starting cluster.
  */
@@ -38,8 +50,8 @@ static struct inode *fat_dget(struct super_block *sb, int i_logstart)
        return inode;
 }
 
-static struct inode *fat_nfs_get_inode(struct super_block *sb,
-                                      u64 ino, u32 generation)
+static struct inode *__fat_nfs_get_inode(struct super_block *sb,
+                                      u64 ino, u32 generation, loff_t i_pos)
 {
        struct inode *inode;
 
@@ -55,35 +67,130 @@ static struct inode *fat_nfs_get_inode(struct super_block *sb,
        return inode;
 }
 
+static struct inode *fat_nfs_get_inode(struct super_block *sb,
+                                      u64 ino, u32 generation)
+{
+
+       return __fat_nfs_get_inode(sb, ino, generation, 0);
+}
+
+static int
+fat_encode_fh_nostale(struct inode *inode, __u32 *fh, int *lenp,
+                     struct inode *parent)
+{
+       int len = *lenp;
+       struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
+       struct fat_fid *fid = (struct fat_fid *) fh;
+       loff_t i_pos;
+       int type = FILEID_FAT_WITHOUT_PARENT;
+
+       if (parent) {
+               if (len < FAT_FID_SIZE_WITH_PARENT) {
+                       *lenp = FAT_FID_SIZE_WITH_PARENT;
+                       return FILEID_INVALID;
+               }
+       } else {
+               if (len < FAT_FID_SIZE_WITHOUT_PARENT) {
+                       *lenp = FAT_FID_SIZE_WITHOUT_PARENT;
+                       return FILEID_INVALID;
+               }
+       }
+
+       i_pos = fat_i_pos_read(sbi, inode);
+       *lenp = FAT_FID_SIZE_WITHOUT_PARENT;
+       fid->i_gen = inode->i_generation;
+       fid->i_pos_low = i_pos & 0xFFFFFFFF;
+       fid->i_pos_hi = (i_pos >> 32) & 0xFFFF;
+       if (parent) {
+               i_pos = fat_i_pos_read(sbi, parent);
+               fid->parent_i_pos_hi = (i_pos >> 32) & 0xFFFF;
+               fid->parent_i_pos_low = i_pos & 0xFFFFFFFF;
+               fid->parent_i_gen = parent->i_generation;
+               type = FILEID_FAT_WITH_PARENT;
+               *lenp = FAT_FID_SIZE_WITH_PARENT;
+       }
+
+       return type;
+}
+
 /**
  * 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,
+static struct dentry *fat_fh_to_dentry(struct super_block *sb, struct fid *fid,
                                int fh_len, int fh_type)
 {
        return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
                                    fat_nfs_get_inode);
 }
 
+static struct dentry *fat_fh_to_dentry_nostale(struct super_block *sb,
+                                              struct fid *fh, int fh_len,
+                                              int fh_type)
+{
+       struct inode *inode = NULL;
+       struct fat_fid *fid = (struct fat_fid *)fh;
+       loff_t i_pos;
+
+       switch (fh_type) {
+       case FILEID_FAT_WITHOUT_PARENT:
+               if (fh_len < FAT_FID_SIZE_WITHOUT_PARENT)
+                       return NULL;
+               break;
+       case FILEID_FAT_WITH_PARENT:
+               if (fh_len < FAT_FID_SIZE_WITH_PARENT)
+                       return NULL;
+               break;
+       default:
+               return NULL;
+       }
+       i_pos = fid->i_pos_hi;
+       i_pos = (i_pos << 32) | (fid->i_pos_low);
+       inode = __fat_nfs_get_inode(sb, 0, fid->i_gen, i_pos);
+
+       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,
+static 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);
 }
 
+static struct dentry *fat_fh_to_parent_nostale(struct super_block *sb,
+                                              struct fid *fh, int fh_len,
+                                              int fh_type)
+{
+       struct inode *inode = NULL;
+       struct fat_fid *fid = (struct fat_fid *)fh;
+       loff_t i_pos;
+
+       if (fh_len < FAT_FID_SIZE_WITH_PARENT)
+               return NULL;
+
+       switch (fh_type) {
+       case FILEID_FAT_WITH_PARENT:
+               i_pos = fid->parent_i_pos_hi;
+               i_pos = (i_pos << 32) | (fid->parent_i_pos_low);
+               inode = __fat_nfs_get_inode(sb, 0, fid->parent_i_gen, i_pos);
+               break;
+       }
+
+       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)
+static struct dentry *fat_get_parent(struct dentry *child_dir)
 {
        struct super_block *sb = child_dir->d_sb;
        struct buffer_head *bh = NULL;
@@ -98,3 +205,16 @@ struct dentry *fat_get_parent(struct dentry *child_dir)
 
        return d_obtain_alias(parent_inode);
 }
+
+const struct export_operations fat_export_ops = {
+       .fh_to_dentry   = fat_fh_to_dentry,
+       .fh_to_parent   = fat_fh_to_parent,
+       .get_parent     = fat_get_parent,
+};
+
+const struct export_operations fat_export_ops_nostale = {
+       .encode_fh      = fat_encode_fh_nostale,
+       .fh_to_dentry   = fat_fh_to_dentry_nostale,
+       .fh_to_parent   = fat_fh_to_parent_nostale,
+       .get_parent     = fat_get_parent,
+};