1 #include <linux/ceph/ceph_debug.h> 2 3 #include <linux/exportfs.h> 4 #include <linux/slab.h> 5 #include <asm/unaligned.h> 6 7 #include "super.h" 8 #include "mds_client.h" 9 10 /* 11 * NFS export support 12 * 13 * NFS re-export of a ceph mount is, at present, only semireliable. 14 * The basic issue is that the Ceph architectures doesn't lend itself 15 * well to generating filehandles that will remain valid forever. 16 * 17 * So, we do our best. If you're lucky, your inode will be in the 18 * client's cache. If it's not, and you have a connectable fh, then 19 * the MDS server may be able to find it for you. Otherwise, you get 20 * ESTALE. 21 * 22 * There are ways to this more reliable, but in the non-connectable fh 23 * case, we won't every work perfectly, and in the connectable case, 24 * some changes are needed on the MDS side to work better. 25 */ 26 27 /* 28 * Basic fh 29 */ 30 struct ceph_nfs_fh { 31 u64 ino; 32 } __attribute__ ((packed)); 33 34 /* 35 * Larger 'connectable' fh that includes parent ino and name hash. 36 * Use this whenever possible, as it works more reliably. 37 */ 38 struct ceph_nfs_confh { 39 u64 ino, parent_ino; 40 u32 parent_name_hash; 41 } __attribute__ ((packed)); 42 43 static int ceph_encode_fh(struct dentry *dentry, u32 *rawfh, int *max_len, 44 int connectable) 45 { 46 int type; 47 struct ceph_nfs_fh *fh = (void *)rawfh; 48 struct ceph_nfs_confh *cfh = (void *)rawfh; 49 struct dentry *parent; 50 struct inode *inode = dentry->d_inode; 51 int connected_handle_length = sizeof(*cfh)/4; 52 int handle_length = sizeof(*fh)/4; 53 54 /* don't re-export snaps */ 55 if (ceph_snap(inode) != CEPH_NOSNAP) 56 return -EINVAL; 57 58 spin_lock(&dentry->d_lock); 59 parent = dentry->d_parent; 60 if (*max_len >= connected_handle_length) { 61 dout("encode_fh %p connectable\n", dentry); 62 cfh->ino = ceph_ino(dentry->d_inode); 63 cfh->parent_ino = ceph_ino(parent->d_inode); 64 cfh->parent_name_hash = ceph_dentry_hash(parent->d_inode, 65 dentry); 66 *max_len = connected_handle_length; 67 type = 2; 68 } else if (*max_len >= handle_length) { 69 if (connectable) { 70 *max_len = connected_handle_length; 71 type = 255; 72 } else { 73 dout("encode_fh %p\n", dentry); 74 fh->ino = ceph_ino(dentry->d_inode); 75 *max_len = handle_length; 76 type = 1; 77 } 78 } else { 79 *max_len = handle_length; 80 type = 255; 81 } 82 spin_unlock(&dentry->d_lock); 83 return type; 84 } 85 86 /* 87 * convert regular fh to dentry 88 * 89 * FIXME: we should try harder by querying the mds for the ino. 90 */ 91 static struct dentry *__fh_to_dentry(struct super_block *sb, 92 struct ceph_nfs_fh *fh) 93 { 94 struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc; 95 struct inode *inode; 96 struct dentry *dentry; 97 struct ceph_vino vino; 98 int err; 99 100 dout("__fh_to_dentry %llx\n", fh->ino); 101 vino.ino = fh->ino; 102 vino.snap = CEPH_NOSNAP; 103 inode = ceph_find_inode(sb, vino); 104 if (!inode) { 105 struct ceph_mds_request *req; 106 107 req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO, 108 USE_ANY_MDS); 109 if (IS_ERR(req)) 110 return ERR_CAST(req); 111 112 req->r_ino1 = vino; 113 req->r_num_caps = 1; 114 err = ceph_mdsc_do_request(mdsc, NULL, req); 115 inode = req->r_target_inode; 116 if (inode) 117 ihold(inode); 118 ceph_mdsc_put_request(req); 119 if (!inode) 120 return ERR_PTR(-ESTALE); 121 } 122 123 dentry = d_obtain_alias(inode); 124 if (IS_ERR(dentry)) { 125 pr_err("fh_to_dentry %llx -- inode %p but ENOMEM\n", 126 fh->ino, inode); 127 iput(inode); 128 return dentry; 129 } 130 err = ceph_init_dentry(dentry); 131 if (err < 0) { 132 iput(inode); 133 return ERR_PTR(err); 134 } 135 dout("__fh_to_dentry %llx %p dentry %p\n", fh->ino, inode, dentry); 136 return dentry; 137 } 138 139 /* 140 * convert connectable fh to dentry 141 */ 142 static struct dentry *__cfh_to_dentry(struct super_block *sb, 143 struct ceph_nfs_confh *cfh) 144 { 145 struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc; 146 struct inode *inode; 147 struct dentry *dentry; 148 struct ceph_vino vino; 149 int err; 150 151 dout("__cfh_to_dentry %llx (%llx/%x)\n", 152 cfh->ino, cfh->parent_ino, cfh->parent_name_hash); 153 154 vino.ino = cfh->ino; 155 vino.snap = CEPH_NOSNAP; 156 inode = ceph_find_inode(sb, vino); 157 if (!inode) { 158 struct ceph_mds_request *req; 159 160 req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPHASH, 161 USE_ANY_MDS); 162 if (IS_ERR(req)) 163 return ERR_CAST(req); 164 165 req->r_ino1 = vino; 166 req->r_ino2.ino = cfh->parent_ino; 167 req->r_ino2.snap = CEPH_NOSNAP; 168 req->r_path2 = kmalloc(16, GFP_NOFS); 169 snprintf(req->r_path2, 16, "%d", cfh->parent_name_hash); 170 req->r_num_caps = 1; 171 err = ceph_mdsc_do_request(mdsc, NULL, req); 172 inode = req->r_target_inode; 173 if (inode) 174 ihold(inode); 175 ceph_mdsc_put_request(req); 176 if (!inode) 177 return ERR_PTR(err ? err : -ESTALE); 178 } 179 180 dentry = d_obtain_alias(inode); 181 if (IS_ERR(dentry)) { 182 pr_err("cfh_to_dentry %llx -- inode %p but ENOMEM\n", 183 cfh->ino, inode); 184 iput(inode); 185 return dentry; 186 } 187 err = ceph_init_dentry(dentry); 188 if (err < 0) { 189 iput(inode); 190 return ERR_PTR(err); 191 } 192 dout("__cfh_to_dentry %llx %p dentry %p\n", cfh->ino, inode, dentry); 193 return dentry; 194 } 195 196 static struct dentry *ceph_fh_to_dentry(struct super_block *sb, struct fid *fid, 197 int fh_len, int fh_type) 198 { 199 if (fh_type == 1) 200 return __fh_to_dentry(sb, (struct ceph_nfs_fh *)fid->raw); 201 else 202 return __cfh_to_dentry(sb, (struct ceph_nfs_confh *)fid->raw); 203 } 204 205 /* 206 * get parent, if possible. 207 * 208 * FIXME: we could do better by querying the mds to discover the 209 * parent. 210 */ 211 static struct dentry *ceph_fh_to_parent(struct super_block *sb, 212 struct fid *fid, 213 int fh_len, int fh_type) 214 { 215 struct ceph_nfs_confh *cfh = (void *)fid->raw; 216 struct ceph_vino vino; 217 struct inode *inode; 218 struct dentry *dentry; 219 int err; 220 221 if (fh_type == 1) 222 return ERR_PTR(-ESTALE); 223 224 pr_debug("fh_to_parent %llx/%d\n", cfh->parent_ino, 225 cfh->parent_name_hash); 226 227 vino.ino = cfh->ino; 228 vino.snap = CEPH_NOSNAP; 229 inode = ceph_find_inode(sb, vino); 230 if (!inode) 231 return ERR_PTR(-ESTALE); 232 233 dentry = d_obtain_alias(inode); 234 if (IS_ERR(dentry)) { 235 pr_err("fh_to_parent %llx -- inode %p but ENOMEM\n", 236 cfh->ino, inode); 237 iput(inode); 238 return dentry; 239 } 240 err = ceph_init_dentry(dentry); 241 if (err < 0) { 242 iput(inode); 243 return ERR_PTR(err); 244 } 245 dout("fh_to_parent %llx %p dentry %p\n", cfh->ino, inode, dentry); 246 return dentry; 247 } 248 249 const struct export_operations ceph_export_ops = { 250 .encode_fh = ceph_encode_fh, 251 .fh_to_dentry = ceph_fh_to_dentry, 252 .fh_to_parent = ceph_fh_to_parent, 253 }; 254