From 3f65000b6b1460a7a23cd83014bb41a68d1a8a19 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Sat, 4 May 2024 14:30:07 -0700 Subject: nfsd: Fix Link conformance with RFC8881 for delegations RFC8881 specifies that, when a Link operation occurs on an NFSv4, that file delegations issued to other clients must be recalled. Discovered during a recent discussion on nfsv4@ietf.org. Although I have not observed a problem caused by not doing the required delegation recall, it is definitely required by the RFC, so this patch makes the server do the recall. Tested during a recent NFSv4 IETF Bakeathon event. MFC after: 1 week --- sys/fs/nfs/nfs_var.h | 2 +- sys/fs/nfsserver/nfs_nfsdport.c | 12 +++++++----- sys/fs/nfsserver/nfs_nfsdserv.c | 11 +++++++++-- 3 files changed, 17 insertions(+), 8 deletions(-) (limited to 'sys') diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h index 578fb3ce1340..950e0c097457 100644 --- a/sys/fs/nfs/nfs_var.h +++ b/sys/fs/nfs/nfs_var.h @@ -713,7 +713,7 @@ int nfsvno_rmdirsub(struct nameidata *, int, struct ucred *, NFSPROC_T *, struct nfsexstuff *); int nfsvno_rename(struct nameidata *, struct nameidata *, u_int32_t, u_int32_t, struct ucred *, NFSPROC_T *); -int nfsvno_link(struct nameidata *, vnode_t, struct ucred *, +int nfsvno_link(struct nameidata *, vnode_t, nfsquad_t, struct ucred *, NFSPROC_T *, struct nfsexstuff *); int nfsvno_fsync(vnode_t, u_int64_t, int, struct ucred *, NFSPROC_T *); int nfsvno_statfs(vnode_t, struct statfs *); diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c index 5eb16564cf00..6f5b2855bcf0 100644 --- a/sys/fs/nfsserver/nfs_nfsdport.c +++ b/sys/fs/nfsserver/nfs_nfsdport.c @@ -1655,8 +1655,8 @@ out1: * Link vnode op. */ int -nfsvno_link(struct nameidata *ndp, struct vnode *vp, struct ucred *cred, - struct thread *p, struct nfsexstuff *exp) +nfsvno_link(struct nameidata *ndp, struct vnode *vp, nfsquad_t clientid, + struct ucred *cred, struct thread *p, struct nfsexstuff *exp) { struct vnode *xp; int error = 0; @@ -1671,9 +1671,11 @@ nfsvno_link(struct nameidata *ndp, struct vnode *vp, struct ucred *cred, } if (!error) { NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY); - if (!VN_IS_DOOMED(vp)) - error = VOP_LINK(ndp->ni_dvp, vp, &ndp->ni_cnd); - else + if (!VN_IS_DOOMED(vp)) { + error = nfsrv_checkremove(vp, 0, NULL, clientid, p); + if (error == 0) + error = VOP_LINK(ndp->ni_dvp, vp, &ndp->ni_cnd); + } else error = EPERM; if (ndp->ni_dvp == vp) { vrele(ndp->ni_dvp); diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c index 8141ee6cbdb6..0c8bda6dc6a6 100644 --- a/sys/fs/nfsserver/nfs_nfsdserv.c +++ b/sys/fs/nfsserver/nfs_nfsdserv.c @@ -1797,6 +1797,7 @@ nfsrvd_link(struct nfsrv_descript *nd, int isdgram, char *bufp; u_long *hashp; struct thread *p = curthread; + nfsquad_t clientid; if (nd->nd_repstat) { nfsrv_postopattr(nd, getret, &at); @@ -1858,8 +1859,14 @@ nfsrvd_link(struct nfsrv_descript *nd, int isdgram, NULL); } } - if (!nd->nd_repstat) - nd->nd_repstat = nfsvno_link(&named, vp, nd->nd_cred, p, exp); + if (!nd->nd_repstat) { + clientid.qval = 0; + if ((nd->nd_flag & (ND_IMPLIEDCLID | ND_NFSV41)) == + (ND_IMPLIEDCLID | ND_NFSV41)) + clientid.qval = nd->nd_clientid.qval; + nd->nd_repstat = nfsvno_link(&named, vp, clientid, nd->nd_cred, + p, exp); + } if (nd->nd_flag & ND_NFSV3) getret = nfsvno_getattr(vp, &at, nd, p, 0, NULL); if (dirp) { -- cgit v1.2.3