11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * fs/isofs/export.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * (C) 2004 Paul Serice - The new inode scheme requires switching 51da177e4SLinus Torvalds * from iget() to iget5_locked() which means 61da177e4SLinus Torvalds * the NFS export operations have to be hand 71da177e4SLinus Torvalds * coded because the default routines rely on 81da177e4SLinus Torvalds * iget(). 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * The following files are helpful: 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * Documentation/filesystems/Exporting 131da177e4SLinus Torvalds * fs/exportfs/expfs.c. 141da177e4SLinus Torvalds */ 151da177e4SLinus Torvalds 1694f2f715SAl Viro #include "isofs.h" 171da177e4SLinus Torvalds 181da177e4SLinus Torvalds static struct dentry * 191da177e4SLinus Torvalds isofs_export_iget(struct super_block *sb, 201da177e4SLinus Torvalds unsigned long block, 211da177e4SLinus Torvalds unsigned long offset, 221da177e4SLinus Torvalds __u32 generation) 231da177e4SLinus Torvalds { 241da177e4SLinus Torvalds struct inode *inode; 2544003728SChristoph Hellwig 261da177e4SLinus Torvalds if (block == 0) 271da177e4SLinus Torvalds return ERR_PTR(-ESTALE); 281da177e4SLinus Torvalds inode = isofs_iget(sb, block, offset); 29c4386c83SDavid Howells if (IS_ERR(inode)) 30c4386c83SDavid Howells return ERR_CAST(inode); 31c4386c83SDavid Howells if (generation && inode->i_generation != generation) { 321da177e4SLinus Torvalds iput(inode); 331da177e4SLinus Torvalds return ERR_PTR(-ESTALE); 341da177e4SLinus Torvalds } 3544003728SChristoph Hellwig return d_obtain_alias(inode); 361da177e4SLinus Torvalds } 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds /* This function is surprisingly simple. The trick is understanding 391da177e4SLinus Torvalds * that "child" is always a directory. So, to find its parent, you 401da177e4SLinus Torvalds * simply need to find its ".." entry, normalize its block and offset, 411da177e4SLinus Torvalds * and return the underlying inode. See the comments for 421da177e4SLinus Torvalds * isofs_normalize_block_and_offset(). */ 431da177e4SLinus Torvalds static struct dentry *isofs_export_get_parent(struct dentry *child) 441da177e4SLinus Torvalds { 451da177e4SLinus Torvalds unsigned long parent_block = 0; 461da177e4SLinus Torvalds unsigned long parent_offset = 0; 471da177e4SLinus Torvalds struct inode *child_inode = child->d_inode; 481da177e4SLinus Torvalds struct iso_inode_info *e_child_inode = ISOFS_I(child_inode); 491da177e4SLinus Torvalds struct iso_directory_record *de = NULL; 501da177e4SLinus Torvalds struct buffer_head * bh = NULL; 511da177e4SLinus Torvalds struct dentry *rv = NULL; 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds /* "child" must always be a directory. */ 541da177e4SLinus Torvalds if (!S_ISDIR(child_inode->i_mode)) { 551da177e4SLinus Torvalds printk(KERN_ERR "isofs: isofs_export_get_parent(): " 561da177e4SLinus Torvalds "child is not a directory!\n"); 571da177e4SLinus Torvalds rv = ERR_PTR(-EACCES); 581da177e4SLinus Torvalds goto out; 591da177e4SLinus Torvalds } 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds /* It is an invariant that the directory offset is zero. If 621da177e4SLinus Torvalds * it is not zero, it means the directory failed to be 631da177e4SLinus Torvalds * normalized for some reason. */ 641da177e4SLinus Torvalds if (e_child_inode->i_iget5_offset != 0) { 651da177e4SLinus Torvalds printk(KERN_ERR "isofs: isofs_export_get_parent(): " 661da177e4SLinus Torvalds "child directory not normalized!\n"); 671da177e4SLinus Torvalds rv = ERR_PTR(-EACCES); 681da177e4SLinus Torvalds goto out; 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds /* The child inode has been normalized such that its 721da177e4SLinus Torvalds * i_iget5_block value points to the "." entry. Fortunately, 731da177e4SLinus Torvalds * the ".." entry is located in the same block. */ 741da177e4SLinus Torvalds parent_block = e_child_inode->i_iget5_block; 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds /* Get the block in question. */ 771da177e4SLinus Torvalds bh = sb_bread(child_inode->i_sb, parent_block); 781da177e4SLinus Torvalds if (bh == NULL) { 791da177e4SLinus Torvalds rv = ERR_PTR(-EACCES); 801da177e4SLinus Torvalds goto out; 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds /* This is the "." entry. */ 841da177e4SLinus Torvalds de = (struct iso_directory_record*)bh->b_data; 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds /* The ".." entry is always the second entry. */ 871da177e4SLinus Torvalds parent_offset = (unsigned long)isonum_711(de->length); 881da177e4SLinus Torvalds de = (struct iso_directory_record*)(bh->b_data + parent_offset); 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds /* Verify it is in fact the ".." entry. */ 911da177e4SLinus Torvalds if ((isonum_711(de->name_len) != 1) || (de->name[0] != 1)) { 921da177e4SLinus Torvalds printk(KERN_ERR "isofs: Unable to find the \"..\" " 931da177e4SLinus Torvalds "directory for NFS.\n"); 941da177e4SLinus Torvalds rv = ERR_PTR(-EACCES); 951da177e4SLinus Torvalds goto out; 961da177e4SLinus Torvalds } 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds /* Normalize */ 991da177e4SLinus Torvalds isofs_normalize_block_and_offset(de, &parent_block, &parent_offset); 1001da177e4SLinus Torvalds 10144003728SChristoph Hellwig rv = d_obtain_alias(isofs_iget(child_inode->i_sb, parent_block, 10244003728SChristoph Hellwig parent_offset)); 1031da177e4SLinus Torvalds out: 10444003728SChristoph Hellwig if (bh) 1051da177e4SLinus Torvalds brelse(bh); 1061da177e4SLinus Torvalds return rv; 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds static int 1101da177e4SLinus Torvalds isofs_export_encode_fh(struct dentry *dentry, 1111da177e4SLinus Torvalds __u32 *fh32, 1121da177e4SLinus Torvalds int *max_len, 1131da177e4SLinus Torvalds int connectable) 1141da177e4SLinus Torvalds { 1151da177e4SLinus Torvalds struct inode * inode = dentry->d_inode; 1161da177e4SLinus Torvalds struct iso_inode_info * ei = ISOFS_I(inode); 1171da177e4SLinus Torvalds int len = *max_len; 1181da177e4SLinus Torvalds int type = 1; 1191da177e4SLinus Torvalds __u16 *fh16 = (__u16*)fh32; 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds /* 1221da177e4SLinus Torvalds * WARNING: max_len is 5 for NFSv2. Because of this 1231da177e4SLinus Torvalds * limitation, we use the lower 16 bits of fh32[1] to hold the 1241da177e4SLinus Torvalds * offset of the inode and the upper 16 bits of fh32[1] to 1251da177e4SLinus Torvalds * hold the offset of the parent. 1261da177e4SLinus Torvalds */ 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds if (len < 3 || (connectable && len < 5)) 1291da177e4SLinus Torvalds return 255; 1301da177e4SLinus Torvalds 1311da177e4SLinus Torvalds len = 3; 1321da177e4SLinus Torvalds fh32[0] = ei->i_iget5_block; 1331da177e4SLinus Torvalds fh16[2] = (__u16)ei->i_iget5_offset; /* fh16 [sic] */ 1341da177e4SLinus Torvalds fh32[2] = inode->i_generation; 1351da177e4SLinus Torvalds if (connectable && !S_ISDIR(inode->i_mode)) { 1361da177e4SLinus Torvalds struct inode *parent; 1371da177e4SLinus Torvalds struct iso_inode_info *eparent; 1381da177e4SLinus Torvalds spin_lock(&dentry->d_lock); 1391da177e4SLinus Torvalds parent = dentry->d_parent->d_inode; 1401da177e4SLinus Torvalds eparent = ISOFS_I(parent); 1411da177e4SLinus Torvalds fh32[3] = eparent->i_iget5_block; 1421da177e4SLinus Torvalds fh16[3] = (__u16)eparent->i_iget5_offset; /* fh16 [sic] */ 1431da177e4SLinus Torvalds fh32[4] = parent->i_generation; 1441da177e4SLinus Torvalds spin_unlock(&dentry->d_lock); 1451da177e4SLinus Torvalds len = 5; 1461da177e4SLinus Torvalds type = 2; 1471da177e4SLinus Torvalds } 1481da177e4SLinus Torvalds *max_len = len; 1491da177e4SLinus Torvalds return type; 1501da177e4SLinus Torvalds } 1511da177e4SLinus Torvalds 152905251a0SChristoph Hellwig struct isofs_fid { 153905251a0SChristoph Hellwig u32 block; 154905251a0SChristoph Hellwig u16 offset; 155905251a0SChristoph Hellwig u16 parent_offset; 156905251a0SChristoph Hellwig u32 generation; 157905251a0SChristoph Hellwig u32 parent_block; 158905251a0SChristoph Hellwig u32 parent_generation; 159905251a0SChristoph Hellwig }; 1601da177e4SLinus Torvalds 161905251a0SChristoph Hellwig static struct dentry *isofs_fh_to_dentry(struct super_block *sb, 162905251a0SChristoph Hellwig struct fid *fid, int fh_len, int fh_type) 1631da177e4SLinus Torvalds { 164905251a0SChristoph Hellwig struct isofs_fid *ifid = (struct isofs_fid *)fid; 1651da177e4SLinus Torvalds 166905251a0SChristoph Hellwig if (fh_len < 3 || fh_type > 2) 1671da177e4SLinus Torvalds return NULL; 1681da177e4SLinus Torvalds 169905251a0SChristoph Hellwig return isofs_export_iget(sb, ifid->block, ifid->offset, 170905251a0SChristoph Hellwig ifid->generation); 1711da177e4SLinus Torvalds } 1721da177e4SLinus Torvalds 173905251a0SChristoph Hellwig static struct dentry *isofs_fh_to_parent(struct super_block *sb, 174905251a0SChristoph Hellwig struct fid *fid, int fh_len, int fh_type) 175905251a0SChristoph Hellwig { 176905251a0SChristoph Hellwig struct isofs_fid *ifid = (struct isofs_fid *)fid; 1771da177e4SLinus Torvalds 178905251a0SChristoph Hellwig if (fh_type != 2) 179905251a0SChristoph Hellwig return NULL; 180905251a0SChristoph Hellwig 181905251a0SChristoph Hellwig return isofs_export_iget(sb, 182905251a0SChristoph Hellwig fh_len > 2 ? ifid->parent_block : 0, 183905251a0SChristoph Hellwig ifid->parent_offset, 184905251a0SChristoph Hellwig fh_len > 4 ? ifid->parent_generation : 0); 185905251a0SChristoph Hellwig } 1861da177e4SLinus Torvalds 18739655164SChristoph Hellwig const struct export_operations isofs_export_ops = { 1881da177e4SLinus Torvalds .encode_fh = isofs_export_encode_fh, 189905251a0SChristoph Hellwig .fh_to_dentry = isofs_fh_to_dentry, 190905251a0SChristoph Hellwig .fh_to_parent = isofs_fh_to_parent, 1911da177e4SLinus Torvalds .get_parent = isofs_export_get_parent, 1921da177e4SLinus Torvalds }; 193