root/fs/ceph/export.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ceph_encode_snapfh
  2. ceph_encode_fh
  3. __lookup_inode
  4. ceph_lookup_inode
  5. __fh_to_dentry
  6. __snapfh_to_dentry
  7. ceph_fh_to_dentry
  8. __get_parent
  9. ceph_get_parent
  10. ceph_fh_to_parent
  11. __get_snap_name
  12. ceph_get_name

   1 // SPDX-License-Identifier: GPL-2.0
   2 #include <linux/ceph/ceph_debug.h>
   3 
   4 #include <linux/exportfs.h>
   5 #include <linux/slab.h>
   6 #include <asm/unaligned.h>
   7 
   8 #include "super.h"
   9 #include "mds_client.h"
  10 
  11 /*
  12  * Basic fh
  13  */
  14 struct ceph_nfs_fh {
  15         u64 ino;
  16 } __attribute__ ((packed));
  17 
  18 /*
  19  * Larger fh that includes parent ino.
  20  */
  21 struct ceph_nfs_confh {
  22         u64 ino, parent_ino;
  23 } __attribute__ ((packed));
  24 
  25 /*
  26  * fh for snapped inode
  27  */
  28 struct ceph_nfs_snapfh {
  29         u64 ino;
  30         u64 snapid;
  31         u64 parent_ino;
  32         u32 hash;
  33 } __attribute__ ((packed));
  34 
  35 static int ceph_encode_snapfh(struct inode *inode, u32 *rawfh, int *max_len,
  36                               struct inode *parent_inode)
  37 {
  38         static const int snap_handle_length =
  39                 sizeof(struct ceph_nfs_snapfh) >> 2;
  40         struct ceph_nfs_snapfh *sfh = (void *)rawfh;
  41         u64 snapid = ceph_snap(inode);
  42         int ret;
  43         bool no_parent = true;
  44 
  45         if (*max_len < snap_handle_length) {
  46                 *max_len = snap_handle_length;
  47                 ret = FILEID_INVALID;
  48                 goto out;
  49         }
  50 
  51         ret =  -EINVAL;
  52         if (snapid != CEPH_SNAPDIR) {
  53                 struct inode *dir;
  54                 struct dentry *dentry = d_find_alias(inode);
  55                 if (!dentry)
  56                         goto out;
  57 
  58                 rcu_read_lock();
  59                 dir = d_inode_rcu(dentry->d_parent);
  60                 if (ceph_snap(dir) != CEPH_SNAPDIR) {
  61                         sfh->parent_ino = ceph_ino(dir);
  62                         sfh->hash = ceph_dentry_hash(dir, dentry);
  63                         no_parent = false;
  64                 }
  65                 rcu_read_unlock();
  66                 dput(dentry);
  67         }
  68 
  69         if (no_parent) {
  70                 if (!S_ISDIR(inode->i_mode))
  71                         goto out;
  72                 sfh->parent_ino = sfh->ino;
  73                 sfh->hash = 0;
  74         }
  75         sfh->ino = ceph_ino(inode);
  76         sfh->snapid = snapid;
  77 
  78         *max_len = snap_handle_length;
  79         ret = FILEID_BTRFS_WITH_PARENT;
  80 out:
  81         dout("encode_snapfh %llx.%llx ret=%d\n", ceph_vinop(inode), ret);
  82         return ret;
  83 }
  84 
  85 static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len,
  86                           struct inode *parent_inode)
  87 {
  88         static const int handle_length =
  89                 sizeof(struct ceph_nfs_fh) >> 2;
  90         static const int connected_handle_length =
  91                 sizeof(struct ceph_nfs_confh) >> 2;
  92         int type;
  93 
  94         if (ceph_snap(inode) != CEPH_NOSNAP)
  95                 return ceph_encode_snapfh(inode, rawfh, max_len, parent_inode);
  96 
  97         if (parent_inode && (*max_len < connected_handle_length)) {
  98                 *max_len = connected_handle_length;
  99                 return FILEID_INVALID;
 100         } else if (*max_len < handle_length) {
 101                 *max_len = handle_length;
 102                 return FILEID_INVALID;
 103         }
 104 
 105         if (parent_inode) {
 106                 struct ceph_nfs_confh *cfh = (void *)rawfh;
 107                 dout("encode_fh %llx with parent %llx\n",
 108                      ceph_ino(inode), ceph_ino(parent_inode));
 109                 cfh->ino = ceph_ino(inode);
 110                 cfh->parent_ino = ceph_ino(parent_inode);
 111                 *max_len = connected_handle_length;
 112                 type = FILEID_INO32_GEN_PARENT;
 113         } else {
 114                 struct ceph_nfs_fh *fh = (void *)rawfh;
 115                 dout("encode_fh %llx\n", ceph_ino(inode));
 116                 fh->ino = ceph_ino(inode);
 117                 *max_len = handle_length;
 118                 type = FILEID_INO32_GEN;
 119         }
 120         return type;
 121 }
 122 
 123 static struct inode *__lookup_inode(struct super_block *sb, u64 ino)
 124 {
 125         struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
 126         struct inode *inode;
 127         struct ceph_vino vino;
 128         int err;
 129 
 130         vino.ino = ino;
 131         vino.snap = CEPH_NOSNAP;
 132         inode = ceph_find_inode(sb, vino);
 133         if (!inode) {
 134                 struct ceph_mds_request *req;
 135                 int mask;
 136 
 137                 req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
 138                                                USE_ANY_MDS);
 139                 if (IS_ERR(req))
 140                         return ERR_CAST(req);
 141 
 142                 mask = CEPH_STAT_CAP_INODE;
 143                 if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
 144                         mask |= CEPH_CAP_XATTR_SHARED;
 145                 req->r_args.lookupino.mask = cpu_to_le32(mask);
 146 
 147                 req->r_ino1 = vino;
 148                 req->r_num_caps = 1;
 149                 err = ceph_mdsc_do_request(mdsc, NULL, req);
 150                 inode = req->r_target_inode;
 151                 if (inode)
 152                         ihold(inode);
 153                 ceph_mdsc_put_request(req);
 154                 if (!inode)
 155                         return err < 0 ? ERR_PTR(err) : ERR_PTR(-ESTALE);
 156         }
 157         return inode;
 158 }
 159 
 160 struct inode *ceph_lookup_inode(struct super_block *sb, u64 ino)
 161 {
 162         struct inode *inode = __lookup_inode(sb, ino);
 163         if (IS_ERR(inode))
 164                 return inode;
 165         if (inode->i_nlink == 0) {
 166                 iput(inode);
 167                 return ERR_PTR(-ESTALE);
 168         }
 169         return inode;
 170 }
 171 
 172 static struct dentry *__fh_to_dentry(struct super_block *sb, u64 ino)
 173 {
 174         struct inode *inode = __lookup_inode(sb, ino);
 175         if (IS_ERR(inode))
 176                 return ERR_CAST(inode);
 177         if (inode->i_nlink == 0) {
 178                 iput(inode);
 179                 return ERR_PTR(-ESTALE);
 180         }
 181         return d_obtain_alias(inode);
 182 }
 183 
 184 static struct dentry *__snapfh_to_dentry(struct super_block *sb,
 185                                           struct ceph_nfs_snapfh *sfh,
 186                                           bool want_parent)
 187 {
 188         struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
 189         struct ceph_mds_request *req;
 190         struct inode *inode;
 191         struct ceph_vino vino;
 192         int mask;
 193         int err;
 194         bool unlinked = false;
 195 
 196         if (want_parent) {
 197                 vino.ino = sfh->parent_ino;
 198                 if (sfh->snapid == CEPH_SNAPDIR)
 199                         vino.snap = CEPH_NOSNAP;
 200                 else if (sfh->ino == sfh->parent_ino)
 201                         vino.snap = CEPH_SNAPDIR;
 202                 else
 203                         vino.snap = sfh->snapid;
 204         } else {
 205                 vino.ino = sfh->ino;
 206                 vino.snap = sfh->snapid;
 207         }
 208         inode = ceph_find_inode(sb, vino);
 209         if (inode)
 210                 return d_obtain_alias(inode);
 211 
 212         req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
 213                                        USE_ANY_MDS);
 214         if (IS_ERR(req))
 215                 return ERR_CAST(req);
 216 
 217         mask = CEPH_STAT_CAP_INODE;
 218         if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
 219                 mask |= CEPH_CAP_XATTR_SHARED;
 220         req->r_args.lookupino.mask = cpu_to_le32(mask);
 221         if (vino.snap < CEPH_NOSNAP) {
 222                 req->r_args.lookupino.snapid = cpu_to_le64(vino.snap);
 223                 if (!want_parent && sfh->ino != sfh->parent_ino) {
 224                         req->r_args.lookupino.parent =
 225                                         cpu_to_le64(sfh->parent_ino);
 226                         req->r_args.lookupino.hash =
 227                                         cpu_to_le32(sfh->hash);
 228                 }
 229         }
 230 
 231         req->r_ino1 = vino;
 232         req->r_num_caps = 1;
 233         err = ceph_mdsc_do_request(mdsc, NULL, req);
 234         inode = req->r_target_inode;
 235         if (inode) {
 236                 if (vino.snap == CEPH_SNAPDIR) {
 237                         if (inode->i_nlink == 0)
 238                                 unlinked = true;
 239                         inode = ceph_get_snapdir(inode);
 240                 } else if (ceph_snap(inode) == vino.snap) {
 241                         ihold(inode);
 242                 } else {
 243                         /* mds does not support lookup snapped inode */
 244                         err = -EOPNOTSUPP;
 245                         inode = NULL;
 246                 }
 247         }
 248         ceph_mdsc_put_request(req);
 249 
 250         if (want_parent) {
 251                 dout("snapfh_to_parent %llx.%llx\n err=%d\n",
 252                      vino.ino, vino.snap, err);
 253         } else {
 254                 dout("snapfh_to_dentry %llx.%llx parent %llx hash %x err=%d",
 255                       vino.ino, vino.snap, sfh->parent_ino, sfh->hash, err);
 256         }
 257         if (!inode)
 258                 return ERR_PTR(-ESTALE);
 259         /* see comments in ceph_get_parent() */
 260         return unlinked ? d_obtain_root(inode) : d_obtain_alias(inode);
 261 }
 262 
 263 /*
 264  * convert regular fh to dentry
 265  */
 266 static struct dentry *ceph_fh_to_dentry(struct super_block *sb,
 267                                         struct fid *fid,
 268                                         int fh_len, int fh_type)
 269 {
 270         struct ceph_nfs_fh *fh = (void *)fid->raw;
 271 
 272         if (fh_type == FILEID_BTRFS_WITH_PARENT) {
 273                 struct ceph_nfs_snapfh *sfh = (void *)fid->raw;
 274                 return __snapfh_to_dentry(sb, sfh, false);
 275         }
 276 
 277         if (fh_type != FILEID_INO32_GEN  &&
 278             fh_type != FILEID_INO32_GEN_PARENT)
 279                 return NULL;
 280         if (fh_len < sizeof(*fh) / 4)
 281                 return NULL;
 282 
 283         dout("fh_to_dentry %llx\n", fh->ino);
 284         return __fh_to_dentry(sb, fh->ino);
 285 }
 286 
 287 static struct dentry *__get_parent(struct super_block *sb,
 288                                    struct dentry *child, u64 ino)
 289 {
 290         struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
 291         struct ceph_mds_request *req;
 292         struct inode *inode;
 293         int mask;
 294         int err;
 295 
 296         req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPPARENT,
 297                                        USE_ANY_MDS);
 298         if (IS_ERR(req))
 299                 return ERR_CAST(req);
 300 
 301         if (child) {
 302                 req->r_inode = d_inode(child);
 303                 ihold(d_inode(child));
 304         } else {
 305                 req->r_ino1 = (struct ceph_vino) {
 306                         .ino = ino,
 307                         .snap = CEPH_NOSNAP,
 308                 };
 309         }
 310 
 311         mask = CEPH_STAT_CAP_INODE;
 312         if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
 313                 mask |= CEPH_CAP_XATTR_SHARED;
 314         req->r_args.getattr.mask = cpu_to_le32(mask);
 315 
 316         req->r_num_caps = 1;
 317         err = ceph_mdsc_do_request(mdsc, NULL, req);
 318         if (err) {
 319                 ceph_mdsc_put_request(req);
 320                 return ERR_PTR(err);
 321         }
 322 
 323         inode = req->r_target_inode;
 324         if (inode)
 325                 ihold(inode);
 326         ceph_mdsc_put_request(req);
 327         if (!inode)
 328                 return ERR_PTR(-ENOENT);
 329 
 330         return d_obtain_alias(inode);
 331 }
 332 
 333 static struct dentry *ceph_get_parent(struct dentry *child)
 334 {
 335         struct inode *inode = d_inode(child);
 336         struct dentry *dn;
 337 
 338         if (ceph_snap(inode) != CEPH_NOSNAP) {
 339                 struct inode* dir;
 340                 bool unlinked = false;
 341                 /* do not support non-directory */
 342                 if (!d_is_dir(child)) {
 343                         dn = ERR_PTR(-EINVAL);
 344                         goto out;
 345                 }
 346                 dir = __lookup_inode(inode->i_sb, ceph_ino(inode));
 347                 if (IS_ERR(dir)) {
 348                         dn = ERR_CAST(dir);
 349                         goto out;
 350                 }
 351                 /* There can be multiple paths to access snapped inode.
 352                  * For simplicity, treat snapdir of head inode as parent */
 353                 if (ceph_snap(inode) != CEPH_SNAPDIR) {
 354                         struct inode *snapdir = ceph_get_snapdir(dir);
 355                         if (dir->i_nlink == 0)
 356                                 unlinked = true;
 357                         iput(dir);
 358                         if (IS_ERR(snapdir)) {
 359                                 dn = ERR_CAST(snapdir);
 360                                 goto out;
 361                         }
 362                         dir = snapdir;
 363                 }
 364                 /* If directory has already been deleted, futher get_parent
 365                  * will fail. Do not mark snapdir dentry as disconnected,
 366                  * this prevent exportfs from doing futher get_parent. */
 367                 if (unlinked)
 368                         dn = d_obtain_root(dir);
 369                 else
 370                         dn = d_obtain_alias(dir);
 371         } else {
 372                 dn = __get_parent(child->d_sb, child, 0);
 373         }
 374 out:
 375         dout("get_parent %p ino %llx.%llx err=%ld\n",
 376              child, ceph_vinop(inode), (long)PTR_ERR_OR_ZERO(dn));
 377         return dn;
 378 }
 379 
 380 /*
 381  * convert regular fh to parent
 382  */
 383 static struct dentry *ceph_fh_to_parent(struct super_block *sb,
 384                                         struct fid *fid,
 385                                         int fh_len, int fh_type)
 386 {
 387         struct ceph_nfs_confh *cfh = (void *)fid->raw;
 388         struct dentry *dentry;
 389 
 390         if (fh_type == FILEID_BTRFS_WITH_PARENT) {
 391                 struct ceph_nfs_snapfh *sfh = (void *)fid->raw;
 392                 return __snapfh_to_dentry(sb, sfh, true);
 393         }
 394 
 395         if (fh_type != FILEID_INO32_GEN_PARENT)
 396                 return NULL;
 397         if (fh_len < sizeof(*cfh) / 4)
 398                 return NULL;
 399 
 400         dout("fh_to_parent %llx\n", cfh->parent_ino);
 401         dentry = __get_parent(sb, NULL, cfh->ino);
 402         if (unlikely(dentry == ERR_PTR(-ENOENT)))
 403                 dentry = __fh_to_dentry(sb, cfh->parent_ino);
 404         return dentry;
 405 }
 406 
 407 static int __get_snap_name(struct dentry *parent, char *name,
 408                            struct dentry *child)
 409 {
 410         struct inode *inode = d_inode(child);
 411         struct inode *dir = d_inode(parent);
 412         struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
 413         struct ceph_mds_request *req = NULL;
 414         char *last_name = NULL;
 415         unsigned next_offset = 2;
 416         int err = -EINVAL;
 417 
 418         if (ceph_ino(inode) != ceph_ino(dir))
 419                 goto out;
 420         if (ceph_snap(inode) == CEPH_SNAPDIR) {
 421                 if (ceph_snap(dir) == CEPH_NOSNAP) {
 422                         strcpy(name, fsc->mount_options->snapdir_name);
 423                         err = 0;
 424                 }
 425                 goto out;
 426         }
 427         if (ceph_snap(dir) != CEPH_SNAPDIR)
 428                 goto out;
 429 
 430         while (1) {
 431                 struct ceph_mds_reply_info_parsed *rinfo;
 432                 struct ceph_mds_reply_dir_entry *rde;
 433                 int i;
 434 
 435                 req = ceph_mdsc_create_request(fsc->mdsc, CEPH_MDS_OP_LSSNAP,
 436                                                USE_AUTH_MDS);
 437                 if (IS_ERR(req)) {
 438                         err = PTR_ERR(req);
 439                         req = NULL;
 440                         goto out;
 441                 }
 442                 err = ceph_alloc_readdir_reply_buffer(req, inode);
 443                 if (err)
 444                         goto out;
 445 
 446                 req->r_direct_mode = USE_AUTH_MDS;
 447                 req->r_readdir_offset = next_offset;
 448                 req->r_args.readdir.flags =
 449                                 cpu_to_le16(CEPH_READDIR_REPLY_BITFLAGS);
 450                 if (last_name) {
 451                         req->r_path2 = last_name;
 452                         last_name = NULL;
 453                 }
 454 
 455                 req->r_inode = dir;
 456                 ihold(dir);
 457                 req->r_dentry = dget(parent);
 458 
 459                 inode_lock(dir);
 460                 err = ceph_mdsc_do_request(fsc->mdsc, NULL, req);
 461                 inode_unlock(dir);
 462 
 463                 if (err < 0)
 464                         goto out;
 465 
 466                 rinfo = &req->r_reply_info;
 467                 for (i = 0; i < rinfo->dir_nr; i++) {
 468                         rde = rinfo->dir_entries + i;
 469                         BUG_ON(!rde->inode.in);
 470                         if (ceph_snap(inode) ==
 471                             le64_to_cpu(rde->inode.in->snapid)) {
 472                                 memcpy(name, rde->name, rde->name_len);
 473                                 name[rde->name_len] = '\0';
 474                                 err = 0;
 475                                 goto out;
 476                         }
 477                 }
 478 
 479                 if (rinfo->dir_end)
 480                         break;
 481 
 482                 BUG_ON(rinfo->dir_nr <= 0);
 483                 rde = rinfo->dir_entries + (rinfo->dir_nr - 1);
 484                 next_offset += rinfo->dir_nr;
 485                 last_name = kstrndup(rde->name, rde->name_len, GFP_KERNEL);
 486                 if (!last_name) {
 487                         err = -ENOMEM;
 488                         goto out;
 489                 }
 490 
 491                 ceph_mdsc_put_request(req);
 492                 req = NULL;
 493         }
 494         err = -ENOENT;
 495 out:
 496         if (req)
 497                 ceph_mdsc_put_request(req);
 498         kfree(last_name);
 499         dout("get_snap_name %p ino %llx.%llx err=%d\n",
 500              child, ceph_vinop(inode), err);
 501         return err;
 502 }
 503 
 504 static int ceph_get_name(struct dentry *parent, char *name,
 505                          struct dentry *child)
 506 {
 507         struct ceph_mds_client *mdsc;
 508         struct ceph_mds_request *req;
 509         struct inode *inode = d_inode(child);
 510         int err;
 511 
 512         if (ceph_snap(inode) != CEPH_NOSNAP)
 513                 return __get_snap_name(parent, name, child);
 514 
 515         mdsc = ceph_inode_to_client(inode)->mdsc;
 516         req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPNAME,
 517                                        USE_ANY_MDS);
 518         if (IS_ERR(req))
 519                 return PTR_ERR(req);
 520 
 521         inode_lock(d_inode(parent));
 522 
 523         req->r_inode = inode;
 524         ihold(inode);
 525         req->r_ino2 = ceph_vino(d_inode(parent));
 526         req->r_parent = d_inode(parent);
 527         set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
 528         req->r_num_caps = 2;
 529         err = ceph_mdsc_do_request(mdsc, NULL, req);
 530 
 531         inode_unlock(d_inode(parent));
 532 
 533         if (!err) {
 534                 struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;
 535                 memcpy(name, rinfo->dname, rinfo->dname_len);
 536                 name[rinfo->dname_len] = 0;
 537                 dout("get_name %p ino %llx.%llx name %s\n",
 538                      child, ceph_vinop(inode), name);
 539         } else {
 540                 dout("get_name %p ino %llx.%llx err %d\n",
 541                      child, ceph_vinop(inode), err);
 542         }
 543 
 544         ceph_mdsc_put_request(req);
 545         return err;
 546 }
 547 
 548 const struct export_operations ceph_export_ops = {
 549         .encode_fh = ceph_encode_fh,
 550         .fh_to_dentry = ceph_fh_to_dentry,
 551         .fh_to_parent = ceph_fh_to_parent,
 552         .get_parent = ceph_get_parent,
 553         .get_name = ceph_get_name,
 554 };

/* [<][>][^][v][top][bottom][index][help] */