1 /* 2 * fs/isofs/export.c 3 * 4 * (C) 2004 Paul Serice - The new inode scheme requires switching 5 * from iget() to iget5_locked() which means 6 * the NFS export operations have to be hand 7 * coded because the default routines rely on 8 * iget(). 9 * 10 * The following files are helpful: 11 * 12 * Documentation/filesystems/Exporting 13 * fs/exportfs/expfs.c. 14 */ 15 16 #include <linux/buffer_head.h> 17 #include <linux/errno.h> 18 #include <linux/fs.h> 19 #include <linux/iso_fs.h> 20 #include <linux/kernel.h> 21 22 static struct dentry * 23 isofs_export_iget(struct super_block *sb, 24 unsigned long block, 25 unsigned long offset, 26 __u32 generation) 27 { 28 struct inode *inode; 29 struct dentry *result; 30 if (block == 0) 31 return ERR_PTR(-ESTALE); 32 inode = isofs_iget(sb, block, offset); 33 if (inode == NULL) 34 return ERR_PTR(-ENOMEM); 35 if (is_bad_inode(inode) 36 || (generation && inode->i_generation != generation)) 37 { 38 iput(inode); 39 return ERR_PTR(-ESTALE); 40 } 41 result = d_alloc_anon(inode); 42 if (!result) { 43 iput(inode); 44 return ERR_PTR(-ENOMEM); 45 } 46 return result; 47 } 48 49 static struct dentry * 50 isofs_export_get_dentry(struct super_block *sb, void *vobjp) 51 { 52 __u32 *objp = vobjp; 53 unsigned long block = objp[0]; 54 unsigned long offset = objp[1]; 55 __u32 generation = objp[2]; 56 return isofs_export_iget(sb, block, offset, generation); 57 } 58 59 /* This function is surprisingly simple. The trick is understanding 60 * that "child" is always a directory. So, to find its parent, you 61 * simply need to find its ".." entry, normalize its block and offset, 62 * and return the underlying inode. See the comments for 63 * isofs_normalize_block_and_offset(). */ 64 static struct dentry *isofs_export_get_parent(struct dentry *child) 65 { 66 unsigned long parent_block = 0; 67 unsigned long parent_offset = 0; 68 struct inode *child_inode = child->d_inode; 69 struct iso_inode_info *e_child_inode = ISOFS_I(child_inode); 70 struct inode *parent_inode = NULL; 71 struct iso_directory_record *de = NULL; 72 struct buffer_head * bh = NULL; 73 struct dentry *rv = NULL; 74 75 /* "child" must always be a directory. */ 76 if (!S_ISDIR(child_inode->i_mode)) { 77 printk(KERN_ERR "isofs: isofs_export_get_parent(): " 78 "child is not a directory!\n"); 79 rv = ERR_PTR(-EACCES); 80 goto out; 81 } 82 83 /* It is an invariant that the directory offset is zero. If 84 * it is not zero, it means the directory failed to be 85 * normalized for some reason. */ 86 if (e_child_inode->i_iget5_offset != 0) { 87 printk(KERN_ERR "isofs: isofs_export_get_parent(): " 88 "child directory not normalized!\n"); 89 rv = ERR_PTR(-EACCES); 90 goto out; 91 } 92 93 /* The child inode has been normalized such that its 94 * i_iget5_block value points to the "." entry. Fortunately, 95 * the ".." entry is located in the same block. */ 96 parent_block = e_child_inode->i_iget5_block; 97 98 /* Get the block in question. */ 99 bh = sb_bread(child_inode->i_sb, parent_block); 100 if (bh == NULL) { 101 rv = ERR_PTR(-EACCES); 102 goto out; 103 } 104 105 /* This is the "." entry. */ 106 de = (struct iso_directory_record*)bh->b_data; 107 108 /* The ".." entry is always the second entry. */ 109 parent_offset = (unsigned long)isonum_711(de->length); 110 de = (struct iso_directory_record*)(bh->b_data + parent_offset); 111 112 /* Verify it is in fact the ".." entry. */ 113 if ((isonum_711(de->name_len) != 1) || (de->name[0] != 1)) { 114 printk(KERN_ERR "isofs: Unable to find the \"..\" " 115 "directory for NFS.\n"); 116 rv = ERR_PTR(-EACCES); 117 goto out; 118 } 119 120 /* Normalize */ 121 isofs_normalize_block_and_offset(de, &parent_block, &parent_offset); 122 123 /* Get the inode. */ 124 parent_inode = isofs_iget(child_inode->i_sb, 125 parent_block, 126 parent_offset); 127 if (parent_inode == NULL) { 128 rv = ERR_PTR(-EACCES); 129 goto out; 130 } 131 132 /* Allocate the dentry. */ 133 rv = d_alloc_anon(parent_inode); 134 if (rv == NULL) { 135 rv = ERR_PTR(-ENOMEM); 136 goto out; 137 } 138 139 out: 140 if (bh) { 141 brelse(bh); 142 } 143 return rv; 144 } 145 146 static int 147 isofs_export_encode_fh(struct dentry *dentry, 148 __u32 *fh32, 149 int *max_len, 150 int connectable) 151 { 152 struct inode * inode = dentry->d_inode; 153 struct iso_inode_info * ei = ISOFS_I(inode); 154 int len = *max_len; 155 int type = 1; 156 __u16 *fh16 = (__u16*)fh32; 157 158 /* 159 * WARNING: max_len is 5 for NFSv2. Because of this 160 * limitation, we use the lower 16 bits of fh32[1] to hold the 161 * offset of the inode and the upper 16 bits of fh32[1] to 162 * hold the offset of the parent. 163 */ 164 165 if (len < 3 || (connectable && len < 5)) 166 return 255; 167 168 len = 3; 169 fh32[0] = ei->i_iget5_block; 170 fh16[2] = (__u16)ei->i_iget5_offset; /* fh16 [sic] */ 171 fh32[2] = inode->i_generation; 172 if (connectable && !S_ISDIR(inode->i_mode)) { 173 struct inode *parent; 174 struct iso_inode_info *eparent; 175 spin_lock(&dentry->d_lock); 176 parent = dentry->d_parent->d_inode; 177 eparent = ISOFS_I(parent); 178 fh32[3] = eparent->i_iget5_block; 179 fh16[3] = (__u16)eparent->i_iget5_offset; /* fh16 [sic] */ 180 fh32[4] = parent->i_generation; 181 spin_unlock(&dentry->d_lock); 182 len = 5; 183 type = 2; 184 } 185 *max_len = len; 186 return type; 187 } 188 189 190 static struct dentry * 191 isofs_export_decode_fh(struct super_block *sb, 192 __u32 *fh32, 193 int fh_len, 194 int fileid_type, 195 int (*acceptable)(void *context, struct dentry *de), 196 void *context) 197 { 198 __u16 *fh16 = (__u16*)fh32; 199 __u32 child[3]; /* The child is what triggered all this. */ 200 __u32 parent[3]; /* The parent is just along for the ride. */ 201 202 if (fh_len < 3 || fileid_type > 2) 203 return NULL; 204 205 child[0] = fh32[0]; 206 child[1] = fh16[2]; /* fh16 [sic] */ 207 child[2] = fh32[2]; 208 209 parent[0] = 0; 210 parent[1] = 0; 211 parent[2] = 0; 212 if (fileid_type == 2) { 213 if (fh_len > 2) parent[0] = fh32[3]; 214 parent[1] = fh16[3]; /* fh16 [sic] */ 215 if (fh_len > 4) parent[2] = fh32[4]; 216 } 217 218 return sb->s_export_op->find_exported_dentry(sb, child, parent, 219 acceptable, context); 220 } 221 222 223 struct export_operations isofs_export_ops = { 224 .decode_fh = isofs_export_decode_fh, 225 .encode_fh = isofs_export_encode_fh, 226 .get_dentry = isofs_export_get_dentry, 227 .get_parent = isofs_export_get_parent, 228 }; 229