#include #include #include #include #include "super.h" #include "mds_client.h" /* * Basic fh */ struct ceph_nfs_fh { u64 ino; } __attribute__ ((packed)); /* * Larger fh that includes parent ino. */ struct ceph_nfs_confh { u64 ino, parent_ino; } __attribute__ ((packed)); static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len, struct inode *parent_inode) { int type; struct ceph_nfs_fh *fh = (void *)rawfh; struct ceph_nfs_confh *cfh = (void *)rawfh; int connected_handle_length = sizeof(*cfh)/4; int handle_length = sizeof(*fh)/4; /* don't re-export snaps */ if (ceph_snap(inode) != CEPH_NOSNAP) return -EINVAL; if (parent_inode && (*max_len < connected_handle_length)) { *max_len = connected_handle_length; return FILEID_INVALID; } else if (*max_len < handle_length) { *max_len = handle_length; return FILEID_INVALID; } if (parent_inode) { dout("encode_fh %llx with parent %llx\n", ceph_ino(inode), ceph_ino(parent_inode)); cfh->ino = ceph_ino(inode); cfh->parent_ino = ceph_ino(parent_inode); *max_len = connected_handle_length; type = FILEID_INO32_GEN_PARENT; } else { dout("encode_fh %llx\n", ceph_ino(inode)); fh->ino = ceph_ino(inode); *max_len = handle_length; type = FILEID_INO32_GEN; } return type; } static struct dentry *__fh_to_dentry(struct super_block *sb, u64 ino) { struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc; struct inode *inode; struct dentry *dentry; struct ceph_vino vino; int err; vino.ino = ino; vino.snap = CEPH_NOSNAP; inode = ceph_find_inode(sb, vino); if (!inode) { struct ceph_mds_request *req; req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO, USE_ANY_MDS); if (IS_ERR(req)) return ERR_CAST(req); req->r_ino1 = vino; req->r_num_caps = 1; err = ceph_mdsc_do_request(mdsc, NULL, req); inode = req->r_target_inode; if (inode) ihold(inode); ceph_mdsc_put_request(req); if (!inode) return ERR_PTR(-ESTALE); } dentry = d_obtain_alias(inode); if (IS_ERR(dentry)) { iput(inode); return dentry; } err = ceph_init_dentry(dentry); if (err < 0) { dput(dentry); return ERR_PTR(err); } dout("__fh_to_dentry %llx %p dentry %p\n", ino, inode, dentry); return dentry; } /* * convert regular fh to dentry */ static struct dentry *ceph_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { struct ceph_nfs_fh *fh = (void *)fid->raw; if (fh_type != FILEID_INO32_GEN && fh_type != FILEID_INO32_GEN_PARENT) return NULL; if (fh_len < sizeof(*fh) / 4) return NULL; dout("fh_to_dentry %llx\n", fh->ino); return __fh_to_dentry(sb, fh->ino); } static struct dentry *__get_parent(struct super_block *sb, struct dentry *child, u64 ino) { struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc; struct ceph_mds_request *req; struct inode *inode; struct dentry *dentry; int err; req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPPARENT, USE_ANY_MDS); if (IS_ERR(req)) return ERR_CAST(req); if (child) { req->r_inode = child->d_inode; ihold(child->d_inode); } else { req->r_ino1 = (struct ceph_vino) { .ino = ino, .snap = CEPH_NOSNAP, }; } req->r_num_caps = 1; err = ceph_mdsc_do_request(mdsc, NULL, req); inode = req->r_target_inode; if (inode) ihold(inode); ceph_mdsc_put_request(req); if (!inode) return ERR_PTR(-ENOENT); dentry = d_obtain_alias(inode); if (IS_ERR(dentry)) { iput(inode); return dentry; } err = ceph_init_dentry(dentry); if (err < 0) { dput(dentry); return ERR_PTR(err); } dout("__get_parent ino %llx parent %p ino %llx.%llx\n", child ? ceph_ino(child->d_inode) : ino, dentry, ceph_vinop(inode)); return dentry; } struct dentry *ceph_get_parent(struct dentry *child) { /* don't re-export snaps */ if (ceph_snap(child->d_inode) != CEPH_NOSNAP) return ERR_PTR(-EINVAL); dout("get_parent %p ino %llx.%llx\n", child, ceph_vinop(child->d_inode)); return __get_parent(child->d_sb, child, 0); } /* * get parent, if possible. * * FIXME: we could do better by querying the mds to discover the * parent. */ static struct dentry *ceph_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len, int fh_type) { struct ceph_nfs_confh *cfh = (void *)fid->raw; struct ceph_vino vino; struct inode *inode; struct dentry *dentry; int err; if (fh_type == 1) return ERR_PTR(-ESTALE); if (fh_len < sizeof(*cfh) / 4) return ERR_PTR(-ESTALE); pr_debug("fh_to_parent %llx/%d\n", cfh->parent_ino, cfh->parent_name_hash); vino.ino = cfh->ino; vino.snap = CEPH_NOSNAP; inode = ceph_find_inode(sb, vino); if (!inode) return ERR_PTR(-ESTALE); dentry = d_obtain_alias(inode); if (IS_ERR(dentry)) { pr_err("fh_to_parent %llx -- inode %p but ENOMEM\n", cfh->ino, inode); iput(inode); return dentry; } err = ceph_init_dentry(dentry); if (err < 0) { iput(inode); return ERR_PTR(err); } dout("fh_to_parent %llx %p dentry %p\n", cfh->ino, inode, dentry); return dentry; } const struct export_operations ceph_export_ops = { .encode_fh = ceph_encode_fh, .fh_to_dentry = ceph_fh_to_dentry, .fh_to_parent = ceph_fh_to_parent, .get_parent = ceph_get_parent, };