Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ceph: pass ino# instead of old_dentry if it's disconnected

When exporting the kceph to NFS it may pass a DCACHE_DISCONNECTED
dentry for the link operation. Then it will parse this dentry as a
snapdir, and the mds will fail the link request as -EROFS.

MDS allow clients to pass a ino# instead of a path.

Link: https://tracker.ceph.com/issues/59515
Signed-off-by: Xiubo Li <xiubli@redhat.com>
Reviewed-by: Ilya Dryomov <idryomov@gmail.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>

authored by

Xiubo Li and committed by
Ilya Dryomov
a5ffd7b6 aaf67de7

+16 -3
+11 -2
fs/ceph/dir.c
··· 1050 1050 struct ceph_mds_request *req; 1051 1051 int err; 1052 1052 1053 + if (dentry->d_flags & DCACHE_DISCONNECTED) 1054 + return -EINVAL; 1055 + 1053 1056 err = ceph_wait_on_conflict_unlink(dentry); 1054 1057 if (err) 1055 1058 return err; ··· 1060 1057 if (ceph_snap(dir) != CEPH_NOSNAP) 1061 1058 return -EROFS; 1062 1059 1063 - dout("link in dir %p old_dentry %p dentry %p\n", dir, 1064 - old_dentry, dentry); 1060 + dout("link in dir %p %llx.%llx old_dentry %p:'%pd' dentry %p:'%pd'\n", 1061 + dir, ceph_vinop(dir), old_dentry, old_dentry, dentry, dentry); 1065 1062 req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LINK, USE_AUTH_MDS); 1066 1063 if (IS_ERR(req)) { 1067 1064 d_drop(dentry); ··· 1070 1067 req->r_dentry = dget(dentry); 1071 1068 req->r_num_caps = 2; 1072 1069 req->r_old_dentry = dget(old_dentry); 1070 + /* 1071 + * The old_dentry maybe a DCACHE_DISCONNECTED dentry, then we 1072 + * will just pass the ino# to MDSs. 1073 + */ 1074 + if (old_dentry->d_flags & DCACHE_DISCONNECTED) 1075 + req->r_ino2 = ceph_vino(d_inode(old_dentry)); 1073 1076 req->r_parent = dir; 1074 1077 ihold(dir); 1075 1078 set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
+5 -1
fs/ceph/mds_client.c
··· 2570 2570 u64 ino1 = 0, ino2 = 0; 2571 2571 int pathlen1 = 0, pathlen2 = 0; 2572 2572 bool freepath1 = false, freepath2 = false; 2573 + struct dentry *old_dentry = NULL; 2573 2574 int len; 2574 2575 u16 releases; 2575 2576 void *p, *end; ··· 2588 2587 } 2589 2588 2590 2589 /* If r_old_dentry is set, then assume that its parent is locked */ 2591 - ret = set_request_path_attr(NULL, req->r_old_dentry, 2590 + if (req->r_old_dentry && 2591 + !(req->r_old_dentry->d_flags & DCACHE_DISCONNECTED)) 2592 + old_dentry = req->r_old_dentry; 2593 + ret = set_request_path_attr(NULL, old_dentry, 2592 2594 req->r_old_dentry_dir, 2593 2595 req->r_path2, req->r_ino2.ino, 2594 2596 &path2, &pathlen2, &ino2, &freepath2, true);