export.c 15 KB

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