linux/fs/ceph/export.c
<<
>>
Prefs
   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 */
  30struct 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 */
  38struct ceph_nfs_confh {
  39        u64 ino, parent_ino;
  40        u32 parent_name_hash;
  41} __attribute__ ((packed));
  42
  43static 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 = dentry->d_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        if (*max_len >= connected_handle_length) {
  59                dout("encode_fh %p connectable\n", dentry);
  60                cfh->ino = ceph_ino(dentry->d_inode);
  61                cfh->parent_ino = ceph_ino(parent->d_inode);
  62                cfh->parent_name_hash = ceph_dentry_hash(parent);
  63                *max_len = connected_handle_length;
  64                type = 2;
  65        } else if (*max_len >= handle_length) {
  66                if (connectable) {
  67                        *max_len = connected_handle_length;
  68                        return 255;
  69                }
  70                dout("encode_fh %p\n", dentry);
  71                fh->ino = ceph_ino(dentry->d_inode);
  72                *max_len = handle_length;
  73                type = 1;
  74        } else {
  75                *max_len = handle_length;
  76                return 255;
  77        }
  78        return type;
  79}
  80
  81/*
  82 * convert regular fh to dentry
  83 *
  84 * FIXME: we should try harder by querying the mds for the ino.
  85 */
  86static struct dentry *__fh_to_dentry(struct super_block *sb,
  87                                     struct ceph_nfs_fh *fh)
  88{
  89        struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
  90        struct inode *inode;
  91        struct dentry *dentry;
  92        struct ceph_vino vino;
  93        int err;
  94
  95        dout("__fh_to_dentry %llx\n", fh->ino);
  96        vino.ino = fh->ino;
  97        vino.snap = CEPH_NOSNAP;
  98        inode = ceph_find_inode(sb, vino);
  99        if (!inode) {
 100                struct ceph_mds_request *req;
 101
 102                req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
 103                                               USE_ANY_MDS);
 104                if (IS_ERR(req))
 105                        return ERR_CAST(req);
 106
 107                req->r_ino1 = vino;
 108                req->r_num_caps = 1;
 109                err = ceph_mdsc_do_request(mdsc, NULL, req);
 110                inode = req->r_target_inode;
 111                if (inode)
 112                        ihold(inode);
 113                ceph_mdsc_put_request(req);
 114                if (!inode)
 115                        return ERR_PTR(-ESTALE);
 116        }
 117
 118        dentry = d_obtain_alias(inode);
 119        if (IS_ERR(dentry)) {
 120                pr_err("fh_to_dentry %llx -- inode %p but ENOMEM\n",
 121                       fh->ino, inode);
 122                iput(inode);
 123                return dentry;
 124        }
 125        err = ceph_init_dentry(dentry);
 126
 127        if (err < 0) {
 128                iput(inode);
 129                return ERR_PTR(err);
 130        }
 131        dout("__fh_to_dentry %llx %p dentry %p\n", fh->ino, inode, dentry);
 132        return dentry;
 133}
 134
 135/*
 136 * convert connectable fh to dentry
 137 */
 138static struct dentry *__cfh_to_dentry(struct super_block *sb,
 139                                      struct ceph_nfs_confh *cfh)
 140{
 141        struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
 142        struct inode *inode;
 143        struct dentry *dentry;
 144        struct ceph_vino vino;
 145        int err;
 146
 147        dout("__cfh_to_dentry %llx (%llx/%x)\n",
 148             cfh->ino, cfh->parent_ino, cfh->parent_name_hash);
 149
 150        vino.ino = cfh->ino;
 151        vino.snap = CEPH_NOSNAP;
 152        inode = ceph_find_inode(sb, vino);
 153        if (!inode) {
 154                struct ceph_mds_request *req;
 155
 156                req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPHASH,
 157                                               USE_ANY_MDS);
 158                if (IS_ERR(req))
 159                        return ERR_CAST(req);
 160
 161                req->r_ino1 = vino;
 162                req->r_ino2.ino = cfh->parent_ino;
 163                req->r_ino2.snap = CEPH_NOSNAP;
 164                req->r_path2 = kmalloc(16, GFP_NOFS);
 165                snprintf(req->r_path2, 16, "%d", cfh->parent_name_hash);
 166                req->r_num_caps = 1;
 167                err = ceph_mdsc_do_request(mdsc, NULL, req);
 168                inode = req->r_target_inode;
 169                if (inode)
 170                        ihold(inode);
 171                ceph_mdsc_put_request(req);
 172                if (!inode)
 173                        return ERR_PTR(err ? err : -ESTALE);
 174        }
 175
 176        dentry = d_obtain_alias(inode);
 177        if (IS_ERR(dentry)) {
 178                pr_err("cfh_to_dentry %llx -- inode %p but ENOMEM\n",
 179                       cfh->ino, inode);
 180                iput(inode);
 181                return dentry;
 182        }
 183        err = ceph_init_dentry(dentry);
 184        if (err < 0) {
 185                iput(inode);
 186                return ERR_PTR(err);
 187        }
 188        dout("__cfh_to_dentry %llx %p dentry %p\n", cfh->ino, inode, dentry);
 189        return dentry;
 190}
 191
 192static struct dentry *ceph_fh_to_dentry(struct super_block *sb, struct fid *fid,
 193                                        int fh_len, int fh_type)
 194{
 195        if (fh_type == 1)
 196                return __fh_to_dentry(sb, (struct ceph_nfs_fh *)fid->raw);
 197        else
 198                return __cfh_to_dentry(sb, (struct ceph_nfs_confh *)fid->raw);
 199}
 200
 201/*
 202 * get parent, if possible.
 203 *
 204 * FIXME: we could do better by querying the mds to discover the
 205 * parent.
 206 */
 207static struct dentry *ceph_fh_to_parent(struct super_block *sb,
 208                                         struct fid *fid,
 209                                        int fh_len, int fh_type)
 210{
 211        struct ceph_nfs_confh *cfh = (void *)fid->raw;
 212        struct ceph_vino vino;
 213        struct inode *inode;
 214        struct dentry *dentry;
 215        int err;
 216
 217        if (fh_type == 1)
 218                return ERR_PTR(-ESTALE);
 219
 220        pr_debug("fh_to_parent %llx/%d\n", cfh->parent_ino,
 221                 cfh->parent_name_hash);
 222
 223        vino.ino = cfh->ino;
 224        vino.snap = CEPH_NOSNAP;
 225        inode = ceph_find_inode(sb, vino);
 226        if (!inode)
 227                return ERR_PTR(-ESTALE);
 228
 229        dentry = d_obtain_alias(inode);
 230        if (IS_ERR(dentry)) {
 231                pr_err("fh_to_parent %llx -- inode %p but ENOMEM\n",
 232                       cfh->ino, inode);
 233                iput(inode);
 234                return dentry;
 235        }
 236        err = ceph_init_dentry(dentry);
 237        if (err < 0) {
 238                iput(inode);
 239                return ERR_PTR(err);
 240        }
 241        dout("fh_to_parent %llx %p dentry %p\n", cfh->ino, inode, dentry);
 242        return dentry;
 243}
 244
 245const struct export_operations ceph_export_ops = {
 246        .encode_fh = ceph_encode_fh,
 247        .fh_to_dentry = ceph_fh_to_dentry,
 248        .fh_to_parent = ceph_fh_to_parent,
 249};
 250