aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRick Macklem <rmacklem@FreeBSD.org>2024-05-04 21:30:07 +0000
committerRick Macklem <rmacklem@FreeBSD.org>2024-05-04 21:30:07 +0000
commit3f65000b6b1460a7a23cd83014bb41a68d1a8a19 (patch)
treeedaa887f177b72f48a91cb3cdcab5c83a31fc4dd
parent6f444019009a55aac18d18054d154155fbf606c9 (diff)
downloadsrc-3f65000b6b1460a7a23cd83014bb41a68d1a8a19.tar.gz
src-3f65000b6b1460a7a23cd83014bb41a68d1a8a19.zip
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
-rw-r--r--sys/fs/nfs/nfs_var.h2
-rw-r--r--sys/fs/nfsserver/nfs_nfsdport.c12
-rw-r--r--sys/fs/nfsserver/nfs_nfsdserv.c11
3 files changed, 17 insertions, 8 deletions
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) {