Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* fs/fat/nfs.c
0003  */
0004 
0005 #include <linux/exportfs.h>
0006 #include "fat.h"
0007 
0008 struct fat_fid {
0009     u32 i_gen;
0010     u32 i_pos_low;
0011     u16 i_pos_hi;
0012     u16 parent_i_pos_hi;
0013     u32 parent_i_pos_low;
0014     u32 parent_i_gen;
0015 };
0016 
0017 #define FAT_FID_SIZE_WITHOUT_PARENT 3
0018 #define FAT_FID_SIZE_WITH_PARENT (sizeof(struct fat_fid)/sizeof(u32))
0019 
0020 /**
0021  * Look up a directory inode given its starting cluster.
0022  */
0023 static struct inode *fat_dget(struct super_block *sb, int i_logstart)
0024 {
0025     struct msdos_sb_info *sbi = MSDOS_SB(sb);
0026     struct hlist_head *head;
0027     struct msdos_inode_info *i;
0028     struct inode *inode = NULL;
0029 
0030     head = sbi->dir_hashtable + fat_dir_hash(i_logstart);
0031     spin_lock(&sbi->dir_hash_lock);
0032     hlist_for_each_entry(i, head, i_dir_hash) {
0033         BUG_ON(i->vfs_inode.i_sb != sb);
0034         if (i->i_logstart != i_logstart)
0035             continue;
0036         inode = igrab(&i->vfs_inode);
0037         if (inode)
0038             break;
0039     }
0040     spin_unlock(&sbi->dir_hash_lock);
0041     return inode;
0042 }
0043 
0044 static struct inode *fat_ilookup(struct super_block *sb, u64 ino, loff_t i_pos)
0045 {
0046     if (MSDOS_SB(sb)->options.nfs == FAT_NFS_NOSTALE_RO)
0047         return fat_iget(sb, i_pos);
0048 
0049     else {
0050         if ((ino < MSDOS_ROOT_INO) || (ino == MSDOS_FSINFO_INO))
0051             return NULL;
0052         return ilookup(sb, ino);
0053     }
0054 }
0055 
0056 static struct inode *__fat_nfs_get_inode(struct super_block *sb,
0057                        u64 ino, u32 generation, loff_t i_pos)
0058 {
0059     struct inode *inode = fat_ilookup(sb, ino, i_pos);
0060 
0061     if (inode && generation && (inode->i_generation != generation)) {
0062         iput(inode);
0063         inode = NULL;
0064     }
0065     if (inode == NULL && MSDOS_SB(sb)->options.nfs == FAT_NFS_NOSTALE_RO) {
0066         struct buffer_head *bh = NULL;
0067         struct msdos_dir_entry *de ;
0068         sector_t blocknr;
0069         int offset;
0070         fat_get_blknr_offset(MSDOS_SB(sb), i_pos, &blocknr, &offset);
0071         bh = sb_bread(sb, blocknr);
0072         if (!bh) {
0073             fat_msg(sb, KERN_ERR,
0074                 "unable to read block(%llu) for building NFS inode",
0075                 (llu)blocknr);
0076             return inode;
0077         }
0078         de = (struct msdos_dir_entry *)bh->b_data;
0079         /* If a file is deleted on server and client is not updated
0080          * yet, we must not build the inode upon a lookup call.
0081          */
0082         if (IS_FREE(de[offset].name))
0083             inode = NULL;
0084         else
0085             inode = fat_build_inode(sb, &de[offset], i_pos);
0086         brelse(bh);
0087     }
0088 
0089     return inode;
0090 }
0091 
0092 static struct inode *fat_nfs_get_inode(struct super_block *sb,
0093                        u64 ino, u32 generation)
0094 {
0095 
0096     return __fat_nfs_get_inode(sb, ino, generation, 0);
0097 }
0098 
0099 static int
0100 fat_encode_fh_nostale(struct inode *inode, __u32 *fh, int *lenp,
0101               struct inode *parent)
0102 {
0103     int len = *lenp;
0104     struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
0105     struct fat_fid *fid = (struct fat_fid *) fh;
0106     loff_t i_pos;
0107     int type = FILEID_FAT_WITHOUT_PARENT;
0108 
0109     if (parent) {
0110         if (len < FAT_FID_SIZE_WITH_PARENT) {
0111             *lenp = FAT_FID_SIZE_WITH_PARENT;
0112             return FILEID_INVALID;
0113         }
0114     } else {
0115         if (len < FAT_FID_SIZE_WITHOUT_PARENT) {
0116             *lenp = FAT_FID_SIZE_WITHOUT_PARENT;
0117             return FILEID_INVALID;
0118         }
0119     }
0120 
0121     i_pos = fat_i_pos_read(sbi, inode);
0122     *lenp = FAT_FID_SIZE_WITHOUT_PARENT;
0123     fid->i_gen = inode->i_generation;
0124     fid->i_pos_low = i_pos & 0xFFFFFFFF;
0125     fid->i_pos_hi = (i_pos >> 32) & 0xFFFF;
0126     if (parent) {
0127         i_pos = fat_i_pos_read(sbi, parent);
0128         fid->parent_i_pos_hi = (i_pos >> 32) & 0xFFFF;
0129         fid->parent_i_pos_low = i_pos & 0xFFFFFFFF;
0130         fid->parent_i_gen = parent->i_generation;
0131         type = FILEID_FAT_WITH_PARENT;
0132         *lenp = FAT_FID_SIZE_WITH_PARENT;
0133     }
0134 
0135     return type;
0136 }
0137 
0138 /**
0139  * Map a NFS file handle to a corresponding dentry.
0140  * The dentry may or may not be connected to the filesystem root.
0141  */
0142 static struct dentry *fat_fh_to_dentry(struct super_block *sb, struct fid *fid,
0143                 int fh_len, int fh_type)
0144 {
0145     return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
0146                     fat_nfs_get_inode);
0147 }
0148 
0149 static struct dentry *fat_fh_to_dentry_nostale(struct super_block *sb,
0150                            struct fid *fh, int fh_len,
0151                            int fh_type)
0152 {
0153     struct inode *inode = NULL;
0154     struct fat_fid *fid = (struct fat_fid *)fh;
0155     loff_t i_pos;
0156 
0157     switch (fh_type) {
0158     case FILEID_FAT_WITHOUT_PARENT:
0159         if (fh_len < FAT_FID_SIZE_WITHOUT_PARENT)
0160             return NULL;
0161         break;
0162     case FILEID_FAT_WITH_PARENT:
0163         if (fh_len < FAT_FID_SIZE_WITH_PARENT)
0164             return NULL;
0165         break;
0166     default:
0167         return NULL;
0168     }
0169     i_pos = fid->i_pos_hi;
0170     i_pos = (i_pos << 32) | (fid->i_pos_low);
0171     inode = __fat_nfs_get_inode(sb, 0, fid->i_gen, i_pos);
0172 
0173     return d_obtain_alias(inode);
0174 }
0175 
0176 /*
0177  * Find the parent for a file specified by NFS handle.
0178  * This requires that the handle contain the i_ino of the parent.
0179  */
0180 static struct dentry *fat_fh_to_parent(struct super_block *sb, struct fid *fid,
0181                 int fh_len, int fh_type)
0182 {
0183     return generic_fh_to_parent(sb, fid, fh_len, fh_type,
0184                     fat_nfs_get_inode);
0185 }
0186 
0187 static struct dentry *fat_fh_to_parent_nostale(struct super_block *sb,
0188                            struct fid *fh, int fh_len,
0189                            int fh_type)
0190 {
0191     struct inode *inode = NULL;
0192     struct fat_fid *fid = (struct fat_fid *)fh;
0193     loff_t i_pos;
0194 
0195     if (fh_len < FAT_FID_SIZE_WITH_PARENT)
0196         return NULL;
0197 
0198     switch (fh_type) {
0199     case FILEID_FAT_WITH_PARENT:
0200         i_pos = fid->parent_i_pos_hi;
0201         i_pos = (i_pos << 32) | (fid->parent_i_pos_low);
0202         inode = __fat_nfs_get_inode(sb, 0, fid->parent_i_gen, i_pos);
0203         break;
0204     }
0205 
0206     return d_obtain_alias(inode);
0207 }
0208 
0209 /*
0210  * Rebuild the parent for a directory that is not connected
0211  *  to the filesystem root
0212  */
0213 static
0214 struct inode *fat_rebuild_parent(struct super_block *sb, int parent_logstart)
0215 {
0216     int search_clus, clus_to_match;
0217     struct msdos_dir_entry *de;
0218     struct inode *parent = NULL;
0219     struct inode *dummy_grand_parent = NULL;
0220     struct fat_slot_info sinfo;
0221     struct msdos_sb_info *sbi = MSDOS_SB(sb);
0222     sector_t blknr = fat_clus_to_blknr(sbi, parent_logstart);
0223     struct buffer_head *parent_bh = sb_bread(sb, blknr);
0224     if (!parent_bh) {
0225         fat_msg(sb, KERN_ERR,
0226             "unable to read cluster of parent directory");
0227         return NULL;
0228     }
0229 
0230     de = (struct msdos_dir_entry *) parent_bh->b_data;
0231     clus_to_match = fat_get_start(sbi, &de[0]);
0232     search_clus = fat_get_start(sbi, &de[1]);
0233 
0234     dummy_grand_parent = fat_dget(sb, search_clus);
0235     if (!dummy_grand_parent) {
0236         dummy_grand_parent = new_inode(sb);
0237         if (!dummy_grand_parent) {
0238             brelse(parent_bh);
0239             return parent;
0240         }
0241 
0242         dummy_grand_parent->i_ino = iunique(sb, MSDOS_ROOT_INO);
0243         fat_fill_inode(dummy_grand_parent, &de[1]);
0244         MSDOS_I(dummy_grand_parent)->i_pos = -1;
0245     }
0246 
0247     if (!fat_scan_logstart(dummy_grand_parent, clus_to_match, &sinfo))
0248         parent = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
0249 
0250     brelse(parent_bh);
0251     iput(dummy_grand_parent);
0252 
0253     return parent;
0254 }
0255 
0256 /*
0257  * Find the parent for a directory that is not currently connected to
0258  * the filesystem root.
0259  *
0260  * On entry, the caller holds d_inode(child_dir)->i_mutex.
0261  */
0262 static struct dentry *fat_get_parent(struct dentry *child_dir)
0263 {
0264     struct super_block *sb = child_dir->d_sb;
0265     struct buffer_head *bh = NULL;
0266     struct msdos_dir_entry *de;
0267     struct inode *parent_inode = NULL;
0268     struct msdos_sb_info *sbi = MSDOS_SB(sb);
0269 
0270     if (!fat_get_dotdot_entry(d_inode(child_dir), &bh, &de)) {
0271         int parent_logstart = fat_get_start(sbi, de);
0272         parent_inode = fat_dget(sb, parent_logstart);
0273         if (!parent_inode && sbi->options.nfs == FAT_NFS_NOSTALE_RO)
0274             parent_inode = fat_rebuild_parent(sb, parent_logstart);
0275     }
0276     brelse(bh);
0277 
0278     return d_obtain_alias(parent_inode);
0279 }
0280 
0281 const struct export_operations fat_export_ops = {
0282     .fh_to_dentry   = fat_fh_to_dentry,
0283     .fh_to_parent   = fat_fh_to_parent,
0284     .get_parent     = fat_get_parent,
0285 };
0286 
0287 const struct export_operations fat_export_ops_nostale = {
0288     .encode_fh      = fat_encode_fh_nostale,
0289     .fh_to_dentry   = fat_fh_to_dentry_nostale,
0290     .fh_to_parent   = fat_fh_to_parent_nostale,
0291     .get_parent     = fat_get_parent,
0292 };