1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/ceph/ceph_debug.h>
4 #include <linux/exportfs.h>
5 #include <linux/slab.h>
6 #include <asm/unaligned.h>
9 #include "mds_client.h"
16 } __attribute__ ((packed
));
19 * Larger fh that includes parent ino.
21 struct ceph_nfs_confh
{
23 } __attribute__ ((packed
));
26 * fh for snapped inode
28 struct ceph_nfs_snapfh
{
33 } __attribute__ ((packed
));
35 static int ceph_encode_snapfh(struct inode
*inode
, u32
*rawfh
, int *max_len
,
36 struct inode
*parent_inode
)
38 const static 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
);
43 bool no_parent
= true;
45 if (*max_len
< snap_handle_length
) {
46 *max_len
= snap_handle_length
;
52 if (snapid
!= CEPH_SNAPDIR
) {
54 struct dentry
*dentry
= d_find_alias(inode
);
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
);
70 if (!S_ISDIR(inode
->i_mode
))
72 sfh
->parent_ino
= sfh
->ino
;
75 sfh
->ino
= ceph_ino(inode
);
78 *max_len
= snap_handle_length
;
79 ret
= FILEID_BTRFS_WITH_PARENT
;
81 dout("encode_snapfh %llx.%llx ret=%d\n", ceph_vinop(inode
), ret
);
85 static int ceph_encode_fh(struct inode
*inode
, u32
*rawfh
, int *max_len
,
86 struct inode
*parent_inode
)
88 const static int handle_length
=
89 sizeof(struct ceph_nfs_fh
) >> 2;
90 const static int connected_handle_length
=
91 sizeof(struct ceph_nfs_confh
) >> 2;
94 if (ceph_snap(inode
) != CEPH_NOSNAP
)
95 return ceph_encode_snapfh(inode
, rawfh
, max_len
, parent_inode
);
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
;
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
;
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
;
123 static struct inode
*__lookup_inode(struct super_block
*sb
, u64 ino
)
125 struct ceph_mds_client
*mdsc
= ceph_sb_to_client(sb
)->mdsc
;
127 struct ceph_vino vino
;
131 vino
.snap
= CEPH_NOSNAP
;
132 inode
= ceph_find_inode(sb
, vino
);
134 struct ceph_mds_request
*req
;
137 req
= ceph_mdsc_create_request(mdsc
, CEPH_MDS_OP_LOOKUPINO
,
140 return ERR_CAST(req
);
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
);
149 err
= ceph_mdsc_do_request(mdsc
, NULL
, req
);
150 inode
= req
->r_target_inode
;
153 ceph_mdsc_put_request(req
);
155 return err
< 0 ? ERR_PTR(err
) : ERR_PTR(-ESTALE
);
160 struct inode
*ceph_lookup_inode(struct super_block
*sb
, u64 ino
)
162 struct inode
*inode
= __lookup_inode(sb
, ino
);
165 if (inode
->i_nlink
== 0) {
167 return ERR_PTR(-ESTALE
);
172 static struct dentry
*__fh_to_dentry(struct super_block
*sb
, u64 ino
)
174 struct inode
*inode
= __lookup_inode(sb
, ino
);
176 return ERR_CAST(inode
);
177 if (inode
->i_nlink
== 0) {
179 return ERR_PTR(-ESTALE
);
181 return d_obtain_alias(inode
);
184 static struct dentry
*__snapfh_to_dentry(struct super_block
*sb
,
185 struct ceph_nfs_snapfh
*sfh
,
188 struct ceph_mds_client
*mdsc
= ceph_sb_to_client(sb
)->mdsc
;
189 struct ceph_mds_request
*req
;
191 struct ceph_vino vino
;
194 bool unlinked
= false;
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
;
203 vino
.snap
= sfh
->snapid
;
206 vino
.snap
= sfh
->snapid
;
208 inode
= ceph_find_inode(sb
, vino
);
210 return d_obtain_alias(inode
);
212 req
= ceph_mdsc_create_request(mdsc
, CEPH_MDS_OP_LOOKUPINO
,
215 return ERR_CAST(req
);
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
);
233 err
= ceph_mdsc_do_request(mdsc
, NULL
, req
);
234 inode
= req
->r_target_inode
;
236 if (vino
.snap
== CEPH_SNAPDIR
) {
237 if (inode
->i_nlink
== 0)
239 inode
= ceph_get_snapdir(inode
);
240 } else if (ceph_snap(inode
) == vino
.snap
) {
243 /* mds does not support lookup snapped inode */
248 ceph_mdsc_put_request(req
);
251 dout("snapfh_to_parent %llx.%llx\n err=%d\n",
252 vino
.ino
, vino
.snap
, err
);
254 dout("snapfh_to_dentry %llx.%llx parent %llx hash %x err=%d",
255 vino
.ino
, vino
.snap
, sfh
->parent_ino
, sfh
->hash
, err
);
258 return ERR_PTR(-ESTALE
);
259 /* see comments in ceph_get_parent() */
260 return unlinked
? d_obtain_root(inode
) : d_obtain_alias(inode
);
264 * convert regular fh to dentry
266 static struct dentry
*ceph_fh_to_dentry(struct super_block
*sb
,
268 int fh_len
, int fh_type
)
270 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);
277 if (fh_type
!= FILEID_INO32_GEN
&&
278 fh_type
!= FILEID_INO32_GEN_PARENT
)
280 if (fh_len
< sizeof(*fh
) / 4)
283 dout("fh_to_dentry %llx\n", fh
->ino
);
284 return __fh_to_dentry(sb
, fh
->ino
);
287 static struct dentry
*__get_parent(struct super_block
*sb
,
288 struct dentry
*child
, u64 ino
)
290 struct ceph_mds_client
*mdsc
= ceph_sb_to_client(sb
)->mdsc
;
291 struct ceph_mds_request
*req
;
296 req
= ceph_mdsc_create_request(mdsc
, CEPH_MDS_OP_LOOKUPPARENT
,
299 return ERR_CAST(req
);
302 req
->r_inode
= d_inode(child
);
303 ihold(d_inode(child
));
305 req
->r_ino1
= (struct ceph_vino
) {
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
);
317 err
= ceph_mdsc_do_request(mdsc
, NULL
, req
);
318 inode
= req
->r_target_inode
;
321 ceph_mdsc_put_request(req
);
323 return ERR_PTR(-ENOENT
);
325 return d_obtain_alias(inode
);
328 static struct dentry
*ceph_get_parent(struct dentry
*child
)
330 struct inode
*inode
= d_inode(child
);
333 if (ceph_snap(inode
) != CEPH_NOSNAP
) {
335 bool unlinked
= false;
336 /* do not support non-directory */
337 if (!d_is_dir(child
)) {
338 dn
= ERR_PTR(-EINVAL
);
341 dir
= __lookup_inode(inode
->i_sb
, ceph_ino(inode
));
346 /* There can be multiple paths to access snapped inode.
347 * For simplicity, treat snapdir of head inode as parent */
348 if (ceph_snap(inode
) != CEPH_SNAPDIR
) {
349 struct inode
*snapdir
= ceph_get_snapdir(dir
);
350 if (dir
->i_nlink
== 0)
353 if (IS_ERR(snapdir
)) {
354 dn
= ERR_CAST(snapdir
);
359 /* If directory has already been deleted, futher get_parent
360 * will fail. Do not mark snapdir dentry as disconnected,
361 * this prevent exportfs from doing futher get_parent. */
363 dn
= d_obtain_root(dir
);
365 dn
= d_obtain_alias(dir
);
367 dn
= __get_parent(child
->d_sb
, child
, 0);
370 dout("get_parent %p ino %llx.%llx err=%ld\n",
371 child
, ceph_vinop(inode
), (long)PTR_ERR_OR_ZERO(dn
));
376 * convert regular fh to parent
378 static struct dentry
*ceph_fh_to_parent(struct super_block
*sb
,
380 int fh_len
, int fh_type
)
382 struct ceph_nfs_confh
*cfh
= (void *)fid
->raw
;
383 struct dentry
*dentry
;
385 if (fh_type
== FILEID_BTRFS_WITH_PARENT
) {
386 struct ceph_nfs_snapfh
*sfh
= (void *)fid
->raw
;
387 return __snapfh_to_dentry(sb
, sfh
, true);
390 if (fh_type
!= FILEID_INO32_GEN_PARENT
)
392 if (fh_len
< sizeof(*cfh
) / 4)
395 dout("fh_to_parent %llx\n", cfh
->parent_ino
);
396 dentry
= __get_parent(sb
, NULL
, cfh
->ino
);
397 if (unlikely(dentry
== ERR_PTR(-ENOENT
)))
398 dentry
= __fh_to_dentry(sb
, cfh
->parent_ino
);
402 static int __get_snap_name(struct dentry
*parent
, char *name
,
403 struct dentry
*child
)
405 struct inode
*inode
= d_inode(child
);
406 struct inode
*dir
= d_inode(parent
);
407 struct ceph_fs_client
*fsc
= ceph_inode_to_client(inode
);
408 struct ceph_mds_request
*req
= NULL
;
409 char *last_name
= NULL
;
410 unsigned next_offset
= 2;
413 if (ceph_ino(inode
) != ceph_ino(dir
))
415 if (ceph_snap(inode
) == CEPH_SNAPDIR
) {
416 if (ceph_snap(dir
) == CEPH_NOSNAP
) {
417 strcpy(name
, fsc
->mount_options
->snapdir_name
);
422 if (ceph_snap(dir
) != CEPH_SNAPDIR
)
426 struct ceph_mds_reply_info_parsed
*rinfo
;
427 struct ceph_mds_reply_dir_entry
*rde
;
430 req
= ceph_mdsc_create_request(fsc
->mdsc
, CEPH_MDS_OP_LSSNAP
,
437 err
= ceph_alloc_readdir_reply_buffer(req
, inode
);
441 req
->r_direct_mode
= USE_AUTH_MDS
;
442 req
->r_readdir_offset
= next_offset
;
443 req
->r_args
.readdir
.flags
=
444 cpu_to_le16(CEPH_READDIR_REPLY_BITFLAGS
);
446 req
->r_path2
= last_name
;
452 req
->r_dentry
= dget(parent
);
455 err
= ceph_mdsc_do_request(fsc
->mdsc
, NULL
, req
);
461 rinfo
= &req
->r_reply_info
;
462 for (i
= 0; i
< rinfo
->dir_nr
; i
++) {
463 rde
= rinfo
->dir_entries
+ i
;
464 BUG_ON(!rde
->inode
.in
);
465 if (ceph_snap(inode
) ==
466 le64_to_cpu(rde
->inode
.in
->snapid
)) {
467 memcpy(name
, rde
->name
, rde
->name_len
);
468 name
[rde
->name_len
] = '\0';
477 BUG_ON(rinfo
->dir_nr
<= 0);
478 rde
= rinfo
->dir_entries
+ (rinfo
->dir_nr
- 1);
479 next_offset
+= rinfo
->dir_nr
;
480 last_name
= kstrndup(rde
->name
, rde
->name_len
, GFP_KERNEL
);
486 ceph_mdsc_put_request(req
);
492 ceph_mdsc_put_request(req
);
494 dout("get_snap_name %p ino %llx.%llx err=%d\n",
495 child
, ceph_vinop(inode
), err
);
499 static int ceph_get_name(struct dentry
*parent
, char *name
,
500 struct dentry
*child
)
502 struct ceph_mds_client
*mdsc
;
503 struct ceph_mds_request
*req
;
504 struct inode
*inode
= d_inode(child
);
507 if (ceph_snap(inode
) != CEPH_NOSNAP
)
508 return __get_snap_name(parent
, name
, child
);
510 mdsc
= ceph_inode_to_client(inode
)->mdsc
;
511 req
= ceph_mdsc_create_request(mdsc
, CEPH_MDS_OP_LOOKUPNAME
,
516 inode_lock(d_inode(parent
));
518 req
->r_inode
= inode
;
520 req
->r_ino2
= ceph_vino(d_inode(parent
));
521 req
->r_parent
= d_inode(parent
);
522 set_bit(CEPH_MDS_R_PARENT_LOCKED
, &req
->r_req_flags
);
524 err
= ceph_mdsc_do_request(mdsc
, NULL
, req
);
526 inode_unlock(d_inode(parent
));
529 struct ceph_mds_reply_info_parsed
*rinfo
= &req
->r_reply_info
;
530 memcpy(name
, rinfo
->dname
, rinfo
->dname_len
);
531 name
[rinfo
->dname_len
] = 0;
532 dout("get_name %p ino %llx.%llx name %s\n",
533 child
, ceph_vinop(inode
), name
);
535 dout("get_name %p ino %llx.%llx err %d\n",
536 child
, ceph_vinop(inode
), err
);
539 ceph_mdsc_put_request(req
);
543 const struct export_operations ceph_export_ops
= {
544 .encode_fh
= ceph_encode_fh
,
545 .fh_to_dentry
= ceph_fh_to_dentry
,
546 .fh_to_parent
= ceph_fh_to_parent
,
547 .get_parent
= ceph_get_parent
,
548 .get_name
= ceph_get_name
,