Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/ceph/ceph_debug.h>
0003 
0004 #include <linux/exportfs.h>
0005 #include <linux/slab.h>
0006 #include <asm/unaligned.h>
0007 
0008 #include "super.h"
0009 #include "mds_client.h"
0010 
0011 /*
0012  * Basic fh
0013  */
0014 struct ceph_nfs_fh {
0015     u64 ino;
0016 } __attribute__ ((packed));
0017 
0018 /*
0019  * Larger fh that includes parent ino.
0020  */
0021 struct ceph_nfs_confh {
0022     u64 ino, parent_ino;
0023 } __attribute__ ((packed));
0024 
0025 /*
0026  * fh for snapped inode
0027  */
0028 struct ceph_nfs_snapfh {
0029     u64 ino;
0030     u64 snapid;
0031     u64 parent_ino;
0032     u32 hash;
0033 } __attribute__ ((packed));
0034 
0035 static int ceph_encode_snapfh(struct inode *inode, u32 *rawfh, int *max_len,
0036                   struct inode *parent_inode)
0037 {
0038     static const int snap_handle_length =
0039         sizeof(struct ceph_nfs_snapfh) >> 2;
0040     struct ceph_nfs_snapfh *sfh = (void *)rawfh;
0041     u64 snapid = ceph_snap(inode);
0042     int ret;
0043     bool no_parent = true;
0044 
0045     if (*max_len < snap_handle_length) {
0046         *max_len = snap_handle_length;
0047         ret = FILEID_INVALID;
0048         goto out;
0049     }
0050 
0051     ret =  -EINVAL;
0052     if (snapid != CEPH_SNAPDIR) {
0053         struct inode *dir;
0054         struct dentry *dentry = d_find_alias(inode);
0055         if (!dentry)
0056             goto out;
0057 
0058         rcu_read_lock();
0059         dir = d_inode_rcu(dentry->d_parent);
0060         if (ceph_snap(dir) != CEPH_SNAPDIR) {
0061             sfh->parent_ino = ceph_ino(dir);
0062             sfh->hash = ceph_dentry_hash(dir, dentry);
0063             no_parent = false;
0064         }
0065         rcu_read_unlock();
0066         dput(dentry);
0067     }
0068 
0069     if (no_parent) {
0070         if (!S_ISDIR(inode->i_mode))
0071             goto out;
0072         sfh->parent_ino = sfh->ino;
0073         sfh->hash = 0;
0074     }
0075     sfh->ino = ceph_ino(inode);
0076     sfh->snapid = snapid;
0077 
0078     *max_len = snap_handle_length;
0079     ret = FILEID_BTRFS_WITH_PARENT;
0080 out:
0081     dout("encode_snapfh %llx.%llx ret=%d\n", ceph_vinop(inode), ret);
0082     return ret;
0083 }
0084 
0085 static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len,
0086               struct inode *parent_inode)
0087 {
0088     static const int handle_length =
0089         sizeof(struct ceph_nfs_fh) >> 2;
0090     static const int connected_handle_length =
0091         sizeof(struct ceph_nfs_confh) >> 2;
0092     int type;
0093 
0094     if (ceph_snap(inode) != CEPH_NOSNAP)
0095         return ceph_encode_snapfh(inode, rawfh, max_len, parent_inode);
0096 
0097     if (parent_inode && (*max_len < connected_handle_length)) {
0098         *max_len = connected_handle_length;
0099         return FILEID_INVALID;
0100     } else if (*max_len < handle_length) {
0101         *max_len = handle_length;
0102         return FILEID_INVALID;
0103     }
0104 
0105     if (parent_inode) {
0106         struct ceph_nfs_confh *cfh = (void *)rawfh;
0107         dout("encode_fh %llx with parent %llx\n",
0108              ceph_ino(inode), ceph_ino(parent_inode));
0109         cfh->ino = ceph_ino(inode);
0110         cfh->parent_ino = ceph_ino(parent_inode);
0111         *max_len = connected_handle_length;
0112         type = FILEID_INO32_GEN_PARENT;
0113     } else {
0114         struct ceph_nfs_fh *fh = (void *)rawfh;
0115         dout("encode_fh %llx\n", ceph_ino(inode));
0116         fh->ino = ceph_ino(inode);
0117         *max_len = handle_length;
0118         type = FILEID_INO32_GEN;
0119     }
0120     return type;
0121 }
0122 
0123 static struct inode *__lookup_inode(struct super_block *sb, u64 ino)
0124 {
0125     struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
0126     struct inode *inode;
0127     struct ceph_vino vino;
0128     int err;
0129 
0130     vino.ino = ino;
0131     vino.snap = CEPH_NOSNAP;
0132 
0133     if (ceph_vino_is_reserved(vino))
0134         return ERR_PTR(-ESTALE);
0135 
0136     inode = ceph_find_inode(sb, vino);
0137     if (!inode) {
0138         struct ceph_mds_request *req;
0139         int mask;
0140 
0141         req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
0142                            USE_ANY_MDS);
0143         if (IS_ERR(req))
0144             return ERR_CAST(req);
0145 
0146         mask = CEPH_STAT_CAP_INODE;
0147         if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
0148             mask |= CEPH_CAP_XATTR_SHARED;
0149         req->r_args.lookupino.mask = cpu_to_le32(mask);
0150 
0151         req->r_ino1 = vino;
0152         req->r_num_caps = 1;
0153         err = ceph_mdsc_do_request(mdsc, NULL, req);
0154         inode = req->r_target_inode;
0155         if (inode)
0156             ihold(inode);
0157         ceph_mdsc_put_request(req);
0158         if (!inode)
0159             return err < 0 ? ERR_PTR(err) : ERR_PTR(-ESTALE);
0160     } else {
0161         if (ceph_inode_is_shutdown(inode)) {
0162             iput(inode);
0163             return ERR_PTR(-ESTALE);
0164         }
0165     }
0166     return inode;
0167 }
0168 
0169 struct inode *ceph_lookup_inode(struct super_block *sb, u64 ino)
0170 {
0171     struct inode *inode = __lookup_inode(sb, ino);
0172     if (IS_ERR(inode))
0173         return inode;
0174     if (inode->i_nlink == 0) {
0175         iput(inode);
0176         return ERR_PTR(-ESTALE);
0177     }
0178     return inode;
0179 }
0180 
0181 static struct dentry *__fh_to_dentry(struct super_block *sb, u64 ino)
0182 {
0183     struct inode *inode = __lookup_inode(sb, ino);
0184     int err;
0185 
0186     if (IS_ERR(inode))
0187         return ERR_CAST(inode);
0188     /* We need LINK caps to reliably check i_nlink */
0189     err = ceph_do_getattr(inode, CEPH_CAP_LINK_SHARED, false);
0190     if (err) {
0191         iput(inode);
0192         return ERR_PTR(err);
0193     }
0194     /* -ESTALE if inode as been unlinked and no file is open */
0195     if ((inode->i_nlink == 0) && (atomic_read(&inode->i_count) == 1)) {
0196         iput(inode);
0197         return ERR_PTR(-ESTALE);
0198     }
0199     return d_obtain_alias(inode);
0200 }
0201 
0202 static struct dentry *__snapfh_to_dentry(struct super_block *sb,
0203                       struct ceph_nfs_snapfh *sfh,
0204                       bool want_parent)
0205 {
0206     struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
0207     struct ceph_mds_request *req;
0208     struct inode *inode;
0209     struct ceph_vino vino;
0210     int mask;
0211     int err;
0212     bool unlinked = false;
0213 
0214     if (want_parent) {
0215         vino.ino = sfh->parent_ino;
0216         if (sfh->snapid == CEPH_SNAPDIR)
0217             vino.snap = CEPH_NOSNAP;
0218         else if (sfh->ino == sfh->parent_ino)
0219             vino.snap = CEPH_SNAPDIR;
0220         else
0221             vino.snap = sfh->snapid;
0222     } else {
0223         vino.ino = sfh->ino;
0224         vino.snap = sfh->snapid;
0225     }
0226 
0227     if (ceph_vino_is_reserved(vino))
0228         return ERR_PTR(-ESTALE);
0229 
0230     inode = ceph_find_inode(sb, vino);
0231     if (inode) {
0232         if (ceph_inode_is_shutdown(inode)) {
0233             iput(inode);
0234             return ERR_PTR(-ESTALE);
0235         }
0236         return d_obtain_alias(inode);
0237     }
0238 
0239     req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
0240                        USE_ANY_MDS);
0241     if (IS_ERR(req))
0242         return ERR_CAST(req);
0243 
0244     mask = CEPH_STAT_CAP_INODE;
0245     if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
0246         mask |= CEPH_CAP_XATTR_SHARED;
0247     req->r_args.lookupino.mask = cpu_to_le32(mask);
0248     if (vino.snap < CEPH_NOSNAP) {
0249         req->r_args.lookupino.snapid = cpu_to_le64(vino.snap);
0250         if (!want_parent && sfh->ino != sfh->parent_ino) {
0251             req->r_args.lookupino.parent =
0252                     cpu_to_le64(sfh->parent_ino);
0253             req->r_args.lookupino.hash =
0254                     cpu_to_le32(sfh->hash);
0255         }
0256     }
0257 
0258     req->r_ino1 = vino;
0259     req->r_num_caps = 1;
0260     err = ceph_mdsc_do_request(mdsc, NULL, req);
0261     inode = req->r_target_inode;
0262     if (inode) {
0263         if (vino.snap == CEPH_SNAPDIR) {
0264             if (inode->i_nlink == 0)
0265                 unlinked = true;
0266             inode = ceph_get_snapdir(inode);
0267         } else if (ceph_snap(inode) == vino.snap) {
0268             ihold(inode);
0269         } else {
0270             /* mds does not support lookup snapped inode */
0271             inode = ERR_PTR(-EOPNOTSUPP);
0272         }
0273     } else {
0274         inode = ERR_PTR(-ESTALE);
0275     }
0276     ceph_mdsc_put_request(req);
0277 
0278     if (want_parent) {
0279         dout("snapfh_to_parent %llx.%llx\n err=%d\n",
0280              vino.ino, vino.snap, err);
0281     } else {
0282         dout("snapfh_to_dentry %llx.%llx parent %llx hash %x err=%d",
0283               vino.ino, vino.snap, sfh->parent_ino, sfh->hash, err);
0284     }
0285     if (IS_ERR(inode))
0286         return ERR_CAST(inode);
0287     /* see comments in ceph_get_parent() */
0288     return unlinked ? d_obtain_root(inode) : d_obtain_alias(inode);
0289 }
0290 
0291 /*
0292  * convert regular fh to dentry
0293  */
0294 static struct dentry *ceph_fh_to_dentry(struct super_block *sb,
0295                     struct fid *fid,
0296                     int fh_len, int fh_type)
0297 {
0298     struct ceph_nfs_fh *fh = (void *)fid->raw;
0299 
0300     if (fh_type == FILEID_BTRFS_WITH_PARENT) {
0301         struct ceph_nfs_snapfh *sfh = (void *)fid->raw;
0302         return __snapfh_to_dentry(sb, sfh, false);
0303     }
0304 
0305     if (fh_type != FILEID_INO32_GEN  &&
0306         fh_type != FILEID_INO32_GEN_PARENT)
0307         return NULL;
0308     if (fh_len < sizeof(*fh) / 4)
0309         return NULL;
0310 
0311     dout("fh_to_dentry %llx\n", fh->ino);
0312     return __fh_to_dentry(sb, fh->ino);
0313 }
0314 
0315 static struct dentry *__get_parent(struct super_block *sb,
0316                    struct dentry *child, u64 ino)
0317 {
0318     struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
0319     struct ceph_mds_request *req;
0320     struct inode *inode;
0321     int mask;
0322     int err;
0323 
0324     req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPPARENT,
0325                        USE_ANY_MDS);
0326     if (IS_ERR(req))
0327         return ERR_CAST(req);
0328 
0329     if (child) {
0330         req->r_inode = d_inode(child);
0331         ihold(d_inode(child));
0332     } else {
0333         req->r_ino1 = (struct ceph_vino) {
0334             .ino = ino,
0335             .snap = CEPH_NOSNAP,
0336         };
0337     }
0338 
0339     mask = CEPH_STAT_CAP_INODE;
0340     if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
0341         mask |= CEPH_CAP_XATTR_SHARED;
0342     req->r_args.getattr.mask = cpu_to_le32(mask);
0343 
0344     req->r_num_caps = 1;
0345     err = ceph_mdsc_do_request(mdsc, NULL, req);
0346     if (err) {
0347         ceph_mdsc_put_request(req);
0348         return ERR_PTR(err);
0349     }
0350 
0351     inode = req->r_target_inode;
0352     if (inode)
0353         ihold(inode);
0354     ceph_mdsc_put_request(req);
0355     if (!inode)
0356         return ERR_PTR(-ENOENT);
0357 
0358     return d_obtain_alias(inode);
0359 }
0360 
0361 static struct dentry *ceph_get_parent(struct dentry *child)
0362 {
0363     struct inode *inode = d_inode(child);
0364     struct dentry *dn;
0365 
0366     if (ceph_snap(inode) != CEPH_NOSNAP) {
0367         struct inode* dir;
0368         bool unlinked = false;
0369         /* do not support non-directory */
0370         if (!d_is_dir(child)) {
0371             dn = ERR_PTR(-EINVAL);
0372             goto out;
0373         }
0374         dir = __lookup_inode(inode->i_sb, ceph_ino(inode));
0375         if (IS_ERR(dir)) {
0376             dn = ERR_CAST(dir);
0377             goto out;
0378         }
0379         /* There can be multiple paths to access snapped inode.
0380          * For simplicity, treat snapdir of head inode as parent */
0381         if (ceph_snap(inode) != CEPH_SNAPDIR) {
0382             struct inode *snapdir = ceph_get_snapdir(dir);
0383             if (dir->i_nlink == 0)
0384                 unlinked = true;
0385             iput(dir);
0386             if (IS_ERR(snapdir)) {
0387                 dn = ERR_CAST(snapdir);
0388                 goto out;
0389             }
0390             dir = snapdir;
0391         }
0392         /* If directory has already been deleted, futher get_parent
0393          * will fail. Do not mark snapdir dentry as disconnected,
0394          * this prevent exportfs from doing futher get_parent. */
0395         if (unlinked)
0396             dn = d_obtain_root(dir);
0397         else
0398             dn = d_obtain_alias(dir);
0399     } else {
0400         dn = __get_parent(child->d_sb, child, 0);
0401     }
0402 out:
0403     dout("get_parent %p ino %llx.%llx err=%ld\n",
0404          child, ceph_vinop(inode), (long)PTR_ERR_OR_ZERO(dn));
0405     return dn;
0406 }
0407 
0408 /*
0409  * convert regular fh to parent
0410  */
0411 static struct dentry *ceph_fh_to_parent(struct super_block *sb,
0412                     struct fid *fid,
0413                     int fh_len, int fh_type)
0414 {
0415     struct ceph_nfs_confh *cfh = (void *)fid->raw;
0416     struct dentry *dentry;
0417 
0418     if (fh_type == FILEID_BTRFS_WITH_PARENT) {
0419         struct ceph_nfs_snapfh *sfh = (void *)fid->raw;
0420         return __snapfh_to_dentry(sb, sfh, true);
0421     }
0422 
0423     if (fh_type != FILEID_INO32_GEN_PARENT)
0424         return NULL;
0425     if (fh_len < sizeof(*cfh) / 4)
0426         return NULL;
0427 
0428     dout("fh_to_parent %llx\n", cfh->parent_ino);
0429     dentry = __get_parent(sb, NULL, cfh->ino);
0430     if (unlikely(dentry == ERR_PTR(-ENOENT)))
0431         dentry = __fh_to_dentry(sb, cfh->parent_ino);
0432     return dentry;
0433 }
0434 
0435 static int __get_snap_name(struct dentry *parent, char *name,
0436                struct dentry *child)
0437 {
0438     struct inode *inode = d_inode(child);
0439     struct inode *dir = d_inode(parent);
0440     struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
0441     struct ceph_mds_request *req = NULL;
0442     char *last_name = NULL;
0443     unsigned next_offset = 2;
0444     int err = -EINVAL;
0445 
0446     if (ceph_ino(inode) != ceph_ino(dir))
0447         goto out;
0448     if (ceph_snap(inode) == CEPH_SNAPDIR) {
0449         if (ceph_snap(dir) == CEPH_NOSNAP) {
0450             strcpy(name, fsc->mount_options->snapdir_name);
0451             err = 0;
0452         }
0453         goto out;
0454     }
0455     if (ceph_snap(dir) != CEPH_SNAPDIR)
0456         goto out;
0457 
0458     while (1) {
0459         struct ceph_mds_reply_info_parsed *rinfo;
0460         struct ceph_mds_reply_dir_entry *rde;
0461         int i;
0462 
0463         req = ceph_mdsc_create_request(fsc->mdsc, CEPH_MDS_OP_LSSNAP,
0464                            USE_AUTH_MDS);
0465         if (IS_ERR(req)) {
0466             err = PTR_ERR(req);
0467             req = NULL;
0468             goto out;
0469         }
0470         err = ceph_alloc_readdir_reply_buffer(req, inode);
0471         if (err)
0472             goto out;
0473 
0474         req->r_direct_mode = USE_AUTH_MDS;
0475         req->r_readdir_offset = next_offset;
0476         req->r_args.readdir.flags =
0477                 cpu_to_le16(CEPH_READDIR_REPLY_BITFLAGS);
0478         if (last_name) {
0479             req->r_path2 = last_name;
0480             last_name = NULL;
0481         }
0482 
0483         req->r_inode = dir;
0484         ihold(dir);
0485         req->r_dentry = dget(parent);
0486 
0487         inode_lock(dir);
0488         err = ceph_mdsc_do_request(fsc->mdsc, NULL, req);
0489         inode_unlock(dir);
0490 
0491         if (err < 0)
0492             goto out;
0493 
0494         rinfo = &req->r_reply_info;
0495         for (i = 0; i < rinfo->dir_nr; i++) {
0496             rde = rinfo->dir_entries + i;
0497             BUG_ON(!rde->inode.in);
0498             if (ceph_snap(inode) ==
0499                 le64_to_cpu(rde->inode.in->snapid)) {
0500                 memcpy(name, rde->name, rde->name_len);
0501                 name[rde->name_len] = '\0';
0502                 err = 0;
0503                 goto out;
0504             }
0505         }
0506 
0507         if (rinfo->dir_end)
0508             break;
0509 
0510         BUG_ON(rinfo->dir_nr <= 0);
0511         rde = rinfo->dir_entries + (rinfo->dir_nr - 1);
0512         next_offset += rinfo->dir_nr;
0513         last_name = kstrndup(rde->name, rde->name_len, GFP_KERNEL);
0514         if (!last_name) {
0515             err = -ENOMEM;
0516             goto out;
0517         }
0518 
0519         ceph_mdsc_put_request(req);
0520         req = NULL;
0521     }
0522     err = -ENOENT;
0523 out:
0524     if (req)
0525         ceph_mdsc_put_request(req);
0526     kfree(last_name);
0527     dout("get_snap_name %p ino %llx.%llx err=%d\n",
0528          child, ceph_vinop(inode), err);
0529     return err;
0530 }
0531 
0532 static int ceph_get_name(struct dentry *parent, char *name,
0533              struct dentry *child)
0534 {
0535     struct ceph_mds_client *mdsc;
0536     struct ceph_mds_request *req;
0537     struct inode *inode = d_inode(child);
0538     int err;
0539 
0540     if (ceph_snap(inode) != CEPH_NOSNAP)
0541         return __get_snap_name(parent, name, child);
0542 
0543     mdsc = ceph_inode_to_client(inode)->mdsc;
0544     req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPNAME,
0545                        USE_ANY_MDS);
0546     if (IS_ERR(req))
0547         return PTR_ERR(req);
0548 
0549     inode_lock(d_inode(parent));
0550 
0551     req->r_inode = inode;
0552     ihold(inode);
0553     req->r_ino2 = ceph_vino(d_inode(parent));
0554     req->r_parent = d_inode(parent);
0555     ihold(req->r_parent);
0556     set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
0557     req->r_num_caps = 2;
0558     err = ceph_mdsc_do_request(mdsc, NULL, req);
0559 
0560     inode_unlock(d_inode(parent));
0561 
0562     if (!err) {
0563         struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;
0564         memcpy(name, rinfo->dname, rinfo->dname_len);
0565         name[rinfo->dname_len] = 0;
0566         dout("get_name %p ino %llx.%llx name %s\n",
0567              child, ceph_vinop(inode), name);
0568     } else {
0569         dout("get_name %p ino %llx.%llx err %d\n",
0570              child, ceph_vinop(inode), err);
0571     }
0572 
0573     ceph_mdsc_put_request(req);
0574     return err;
0575 }
0576 
0577 const struct export_operations ceph_export_ops = {
0578     .encode_fh = ceph_encode_fh,
0579     .fh_to_dentry = ceph_fh_to_dentry,
0580     .fh_to_parent = ceph_fh_to_parent,
0581     .get_parent = ceph_get_parent,
0582     .get_name = ceph_get_name,
0583 };