1/* 2 * @TAG(OTHER_GPL) 3 */ 4 5/* fs/fat/nfs.c 6 * 7 * This software is licensed under the terms of the GNU General Public 8 * License version 2, as published by the Free Software Foundation, and 9 * may be copied, distributed, and modified under those terms. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 */ 17 18#include <linux/exportfs.h> 19#include "fat.h" 20 21struct fat_fid { 22 u32 i_gen; 23 u32 i_pos_low; 24 u16 i_pos_hi; 25 u16 parent_i_pos_hi; 26 u32 parent_i_pos_low; 27 u32 parent_i_gen; 28}; 29 30#define FAT_FID_SIZE_WITHOUT_PARENT 3 31#define FAT_FID_SIZE_WITH_PARENT (sizeof(struct fat_fid)/sizeof(u32)) 32 33/** 34 * Look up a directory inode given its starting cluster. 35 */ 36static struct inode *fat_dget(struct super_block *sb, int i_logstart) 37{ 38 struct msdos_sb_info *sbi = MSDOS_SB(sb); 39 struct hlist_head *head; 40 struct msdos_inode_info *i; 41 struct inode *inode = NULL; 42 43 head = sbi->dir_hashtable + fat_dir_hash(i_logstart); 44 spin_lock(&sbi->dir_hash_lock); 45 hlist_for_each_entry(i, head, i_dir_hash) { 46 BUG_ON(i->vfs_inode.i_sb != sb); 47 if (i->i_logstart != i_logstart) 48 continue; 49 inode = igrab(&i->vfs_inode); 50 if (inode) 51 break; 52 } 53 spin_unlock(&sbi->dir_hash_lock); 54 return inode; 55} 56 57static struct inode *fat_ilookup(struct super_block *sb, u64 ino, loff_t i_pos) 58{ 59 if (MSDOS_SB(sb)->options.nfs == FAT_NFS_NOSTALE_RO) 60 return fat_iget(sb, i_pos); 61 62 else { 63 if ((ino < MSDOS_ROOT_INO) || (ino == MSDOS_FSINFO_INO)) 64 return NULL; 65 return ilookup(sb, ino); 66 } 67} 68 69static struct inode *__fat_nfs_get_inode(struct super_block *sb, 70 u64 ino, u32 generation, loff_t i_pos) 71{ 72 struct inode *inode = fat_ilookup(sb, ino, i_pos); 73 74 if (inode && generation && (inode->i_generation != generation)) { 75 iput(inode); 76 inode = NULL; 77 } 78 if (inode == NULL && MSDOS_SB(sb)->options.nfs == FAT_NFS_NOSTALE_RO) { 79 struct buffer_head *bh = NULL; 80 struct msdos_dir_entry *de ; 81 sector_t blocknr; 82 int offset; 83 fat_get_blknr_offset(MSDOS_SB(sb), i_pos, &blocknr, &offset); 84 bh = sb_bread(sb, blocknr); 85 if (!bh) { 86 fat_msg(sb, KERN_ERR, 87 "unable to read block(%llu) for building NFS inode", 88 (llu)blocknr); 89 return inode; 90 } 91 de = (struct msdos_dir_entry *)bh->b_data; 92 /* If a file is deleted on server and client is not updated 93 * yet, we must not build the inode upon a lookup call. 94 */ 95 if (IS_FREE(de[offset].name)) 96 inode = NULL; 97 else 98 inode = fat_build_inode(sb, &de[offset], i_pos); 99 brelse(bh); 100 } 101 102 return inode; 103} 104 105static struct inode *fat_nfs_get_inode(struct super_block *sb, 106 u64 ino, u32 generation) 107{ 108 109 return __fat_nfs_get_inode(sb, ino, generation, 0); 110} 111 112static int 113fat_encode_fh_nostale(struct inode *inode, __u32 *fh, int *lenp, 114 struct inode *parent) 115{ 116 int len = *lenp; 117 struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); 118 struct fat_fid *fid = (struct fat_fid *) fh; 119 loff_t i_pos; 120 int type = FILEID_FAT_WITHOUT_PARENT; 121 122 if (parent) { 123 if (len < FAT_FID_SIZE_WITH_PARENT) { 124 *lenp = FAT_FID_SIZE_WITH_PARENT; 125 return FILEID_INVALID; 126 } 127 } else { 128 if (len < FAT_FID_SIZE_WITHOUT_PARENT) { 129 *lenp = FAT_FID_SIZE_WITHOUT_PARENT; 130 return FILEID_INVALID; 131 } 132 } 133 134 i_pos = fat_i_pos_read(sbi, inode); 135 *lenp = FAT_FID_SIZE_WITHOUT_PARENT; 136 fid->i_gen = inode->i_generation; 137 fid->i_pos_low = i_pos & 0xFFFFFFFF; 138 fid->i_pos_hi = (i_pos >> 32) & 0xFFFF; 139 if (parent) { 140 i_pos = fat_i_pos_read(sbi, parent); 141 fid->parent_i_pos_hi = (i_pos >> 32) & 0xFFFF; 142 fid->parent_i_pos_low = i_pos & 0xFFFFFFFF; 143 fid->parent_i_gen = parent->i_generation; 144 type = FILEID_FAT_WITH_PARENT; 145 *lenp = FAT_FID_SIZE_WITH_PARENT; 146 } 147 148 return type; 149} 150 151/** 152 * Map a NFS file handle to a corresponding dentry. 153 * The dentry may or may not be connected to the filesystem root. 154 */ 155static struct dentry *fat_fh_to_dentry(struct super_block *sb, struct fid *fid, 156 int fh_len, int fh_type) 157{ 158 return generic_fh_to_dentry(sb, fid, fh_len, fh_type, 159 fat_nfs_get_inode); 160} 161 162static struct dentry *fat_fh_to_dentry_nostale(struct super_block *sb, 163 struct fid *fh, int fh_len, 164 int fh_type) 165{ 166 struct inode *inode = NULL; 167 struct fat_fid *fid = (struct fat_fid *)fh; 168 loff_t i_pos; 169 170 switch (fh_type) { 171 case FILEID_FAT_WITHOUT_PARENT: 172 if (fh_len < FAT_FID_SIZE_WITHOUT_PARENT) 173 return NULL; 174 break; 175 case FILEID_FAT_WITH_PARENT: 176 if (fh_len < FAT_FID_SIZE_WITH_PARENT) 177 return NULL; 178 break; 179 default: 180 return NULL; 181 } 182 i_pos = fid->i_pos_hi; 183 i_pos = (i_pos << 32) | (fid->i_pos_low); 184 inode = __fat_nfs_get_inode(sb, 0, fid->i_gen, i_pos); 185 186 return d_obtain_alias(inode); 187} 188 189/* 190 * Find the parent for a file specified by NFS handle. 191 * This requires that the handle contain the i_ino of the parent. 192 */ 193static struct dentry *fat_fh_to_parent(struct super_block *sb, struct fid *fid, 194 int fh_len, int fh_type) 195{ 196 return generic_fh_to_parent(sb, fid, fh_len, fh_type, 197 fat_nfs_get_inode); 198} 199 200static struct dentry *fat_fh_to_parent_nostale(struct super_block *sb, 201 struct fid *fh, int fh_len, 202 int fh_type) 203{ 204 struct inode *inode = NULL; 205 struct fat_fid *fid = (struct fat_fid *)fh; 206 loff_t i_pos; 207 208 if (fh_len < FAT_FID_SIZE_WITH_PARENT) 209 return NULL; 210 211 switch (fh_type) { 212 case FILEID_FAT_WITH_PARENT: 213 i_pos = fid->parent_i_pos_hi; 214 i_pos = (i_pos << 32) | (fid->parent_i_pos_low); 215 inode = __fat_nfs_get_inode(sb, 0, fid->parent_i_gen, i_pos); 216 break; 217 } 218 219 return d_obtain_alias(inode); 220} 221 222/* 223 * Rebuild the parent for a directory that is not connected 224 * to the filesystem root 225 */ 226static 227struct inode *fat_rebuild_parent(struct super_block *sb, int parent_logstart) 228{ 229 int search_clus, clus_to_match; 230 struct msdos_dir_entry *de; 231 struct inode *parent = NULL; 232 struct inode *dummy_grand_parent = NULL; 233 struct fat_slot_info sinfo; 234 struct msdos_sb_info *sbi = MSDOS_SB(sb); 235 sector_t blknr = fat_clus_to_blknr(sbi, parent_logstart); 236 struct buffer_head *parent_bh = sb_bread(sb, blknr); 237 if (!parent_bh) { 238 fat_msg(sb, KERN_ERR, 239 "unable to read cluster of parent directory"); 240 return NULL; 241 } 242 243 de = (struct msdos_dir_entry *) parent_bh->b_data; 244 clus_to_match = fat_get_start(sbi, &de[0]); 245 search_clus = fat_get_start(sbi, &de[1]); 246 247 dummy_grand_parent = fat_dget(sb, search_clus); 248 if (!dummy_grand_parent) { 249 dummy_grand_parent = new_inode(sb); 250 if (!dummy_grand_parent) { 251 brelse(parent_bh); 252 return parent; 253 } 254 255 dummy_grand_parent->i_ino = iunique(sb, MSDOS_ROOT_INO); 256 fat_fill_inode(dummy_grand_parent, &de[1]); 257 MSDOS_I(dummy_grand_parent)->i_pos = -1; 258 } 259 260 if (!fat_scan_logstart(dummy_grand_parent, clus_to_match, &sinfo)) 261 parent = fat_build_inode(sb, sinfo.de, sinfo.i_pos); 262 263 brelse(parent_bh); 264 iput(dummy_grand_parent); 265 266 return parent; 267} 268 269/* 270 * Find the parent for a directory that is not currently connected to 271 * the filesystem root. 272 * 273 * On entry, the caller holds lock on d_inode(child_dir) 274 */ 275static struct dentry *fat_get_parent(struct dentry *child_dir) 276{ 277 struct super_block *sb = child_dir->d_sb; 278 struct buffer_head *bh = NULL; 279 struct msdos_dir_entry *de; 280 struct inode *parent_inode = NULL; 281 struct msdos_sb_info *sbi = MSDOS_SB(sb); 282 283 if (!fat_get_dotdot_entry(d_inode(child_dir), &bh, &de)) { 284 int parent_logstart = fat_get_start(sbi, de); 285 parent_inode = fat_dget(sb, parent_logstart); 286 if (!parent_inode && sbi->options.nfs == FAT_NFS_NOSTALE_RO) 287 parent_inode = fat_rebuild_parent(sb, parent_logstart); 288 } 289 brelse(bh); 290 291 return d_obtain_alias(parent_inode); 292} 293 294const struct export_operations fat_export_ops = { 295 .fh_to_dentry = fat_fh_to_dentry, 296 .fh_to_parent = fat_fh_to_parent, 297 .get_parent = fat_get_parent, 298}; 299 300const struct export_operations fat_export_ops_nostale = { 301 .encode_fh = fat_encode_fh_nostale, 302 .fh_to_dentry = fat_fh_to_dentry_nostale, 303 .fh_to_parent = fat_fh_to_parent_nostale, 304 .get_parent = fat_get_parent, 305}; 306