git: 196787f79e67 - main - nfscl: Use Claim_Null_FH and Claim_Deleg_Cur_FH
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 20 Oct 2023 23:12:33 UTC
The branch main has been updated by rmacklem: URL: https://cgit.FreeBSD.org/src/commit/?id=196787f79e67374527a1d528a42efa8b31acd9af commit 196787f79e67374527a1d528a42efa8b31acd9af Author: Rick Macklem <rmacklem@FreeBSD.org> AuthorDate: 2023-10-20 23:10:25 +0000 Commit: Rick Macklem <rmacklem@FreeBSD.org> CommitDate: 2023-10-20 23:10:25 +0000 nfscl: Use Claim_Null_FH and Claim_Deleg_Cur_FH For NFSv4.1/4.2, there are two new options for the Open operation. These two options use the file handle for the file instead of the file handle for the directory plus a file name. By doing so, the client code is simplified (it no longer needs the "nfsv4node" structure attached to the NFS vnode). It also avoids problems caused by another NFS client (or process running locally in the NFS server) doing a rename or remove of the file name between the Lookup and Open. Unfortunately, there was a bug (fixed recently by commit X) in the NFS server which mis-parsed the Claim_Deleg_Cur_FH arguments. To allow this patch to work with the broken FreeBSD NFSv4.1/4.2 server, NFSMNTP_BUGGYFBSDSRV is defined and is set when a correctly formatted Claim_Deleg_Cur_FH fails with NFSERR_EXPIRED. (This is what the old, broken NFS server does, since it erroneously uses the Getattr arguments as a stateID.) Once this flag is set, the client fills in a stateID, to make the broken NFS server happy. Tested at a recent IETF NFSv4 Bakeathon. MFC after: 1 month --- sys/fs/nfsclient/nfs_clport.c | 4 +- sys/fs/nfsclient/nfs_clrpcops.c | 92 +++++++++++++++++++++++++++++++---------- sys/fs/nfsclient/nfs_clstate.c | 47 +++++++++++++++++---- sys/fs/nfsclient/nfs_clvnops.c | 16 ------- sys/fs/nfsclient/nfsmount.h | 1 + 5 files changed, 114 insertions(+), 46 deletions(-) diff --git a/sys/fs/nfsclient/nfs_clport.c b/sys/fs/nfsclient/nfs_clport.c index 8ea50d80ae19..c0318b692d86 100644 --- a/sys/fs/nfsclient/nfs_clport.c +++ b/sys/fs/nfsclient/nfs_clport.c @@ -264,10 +264,10 @@ nfscl_nget(struct mount *mntp, struct vnode *dvp, struct nfsfh *nfhp, np->n_fhp = nfhp; /* - * For NFSv4, we have to attach the directory file handle and + * For NFSv4.0, we have to attach the directory file handle and * file name, so that Open Ops can be done later. */ - if (nmp->nm_flag & NFSMNT_NFSV4) { + if (NFSHASNFSV4(nmp) && !NFSHASNFSV4N(nmp)) { np->n_v4 = malloc(sizeof (struct nfsv4node) + dnp->n_fhp->nfh_len + cnp->cn_namelen - 1, M_NFSV4NODE, M_WAITOK); diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c index 14351d915ba2..2276e09f6e7e 100644 --- a/sys/fs/nfsclient/nfs_clrpcops.c +++ b/sys/fs/nfsclient/nfs_clrpcops.c @@ -392,16 +392,6 @@ nfsrpc_open(vnode_t vp, int amode, struct ucred *cred, NFSPROC_T *p) nfhp = np->n_fhp; retrycnt = 0; -#ifdef notdef -{ char name[100]; int namel; -namel = (np->n_v4->n4_namelen < 100) ? np->n_v4->n4_namelen : 99; -bcopy(NFS4NODENAME(np->n_v4), name, namel); -name[namel] = '\0'; -printf("rpcopen p=0x%x name=%s",p->p_pid,name); -if (nfhp->nfh_len > 0) printf(" fh=0x%x\n",nfhp->nfh_fh[12]); -else printf(" fhl=0\n"); -} -#endif do { dp = NULL; error = nfscl_open(vp, nfhp->nfh_fh, nfhp->nfh_len, mode, 1, @@ -452,6 +442,39 @@ else printf(" fhl=0\n"); op->nfso_own->nfsow_clp, nfhp->nfh_fh, nfhp->nfh_len, cred, p, &dp); } + } else if (NFSHASNFSV4N(nmp)) { + /* + * For the first attempt, try and get a layout, if + * pNFS is enabled for the mount. + */ + if (!NFSHASPNFS(nmp) || nfscl_enablecallb == 0 || + nfs_numnfscbd == 0 || + (np->n_flag & NNOLAYOUT) != 0 || retrycnt > 0) + error = nfsrpc_openrpc(nmp, vp, nfhp->nfh_fh, + nfhp->nfh_len, nfhp->nfh_fh, nfhp->nfh_len, + mode, op, NULL, 0, &dp, 0, 0x0, cred, p, 0, + 0); + else + error = nfsrpc_getopenlayout(nmp, vp, + nfhp->nfh_fh, nfhp->nfh_len, nfhp->nfh_fh, + nfhp->nfh_len, mode, op, NULL, 0, &dp, + cred, p); + if (dp != NULL) { + NFSLOCKNODE(np); + np->n_flag &= ~NDELEGMOD; + /* + * Invalidate the attribute cache, so that + * attributes that pre-date the issue of a + * delegation are not cached, since the + * cached attributes will remain valid while + * the delegation is held. + */ + NFSINVALATTRCACHE(np); + NFSUNLOCKNODE(np); + (void) nfscl_deleg(nmp->nm_mountp, + op->nfso_own->nfsow_clp, + nfhp->nfh_fh, nfhp->nfh_len, cred, p, &dp); + } } else { error = EIO; } @@ -538,19 +561,40 @@ nfsrpc_openrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, int fhlen, *tl = txdr_unsigned(delegtype); } else { if (dp != NULL) { - *tl = txdr_unsigned(NFSV4OPEN_CLAIMDELEGATECUR); - NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID); - if (NFSHASNFSV4N(nmp)) - *tl++ = 0; - else + if (NFSHASNFSV4N(nmp)) { + *tl = txdr_unsigned( + NFSV4OPEN_CLAIMDELEGATECURFH); + NFSLOCKMNT(nmp); + if ((nmp->nm_privflag & NFSMNTP_BUGGYFBSDSRV) != + 0) { + NFSUNLOCKMNT(nmp); + /* + * Add a stateID argument to make old + * broken FreeBSD NFSv4.1/4.2 servers + * happy. + */ + NFSM_BUILD(tl, uint32_t *,NFSX_STATEID); + *tl++ = 0; + *tl++ = dp->nfsdl_stateid.other[0]; + *tl++ = dp->nfsdl_stateid.other[1]; + *tl = dp->nfsdl_stateid.other[2]; + } else + NFSUNLOCKMNT(nmp); + } else { + *tl = txdr_unsigned(NFSV4OPEN_CLAIMDELEGATECUR); + NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID); *tl++ = dp->nfsdl_stateid.seqid; - *tl++ = dp->nfsdl_stateid.other[0]; - *tl++ = dp->nfsdl_stateid.other[1]; - *tl = dp->nfsdl_stateid.other[2]; + *tl++ = dp->nfsdl_stateid.other[0]; + *tl++ = dp->nfsdl_stateid.other[1]; + *tl = dp->nfsdl_stateid.other[2]; + (void)nfsm_strtom(nd, name, namelen); + } + } else if (NFSHASNFSV4N(nmp)) { + *tl = txdr_unsigned(NFSV4OPEN_CLAIMFH); } else { *tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL); + (void)nfsm_strtom(nd, name, namelen); } - (void) nfsm_strtom(nd, name, namelen); } NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); *tl = txdr_unsigned(NFSV4OP_GETATTR); @@ -2713,6 +2757,8 @@ nfsrpc_createv4(vnode_t dvp, char *name, int namelen, struct vattr *vap, if ((rflags & NFSV4OPEN_RESULTCONFIRM) && (owp->nfsow_clp->nfsc_flags & NFSCLFLAGS_GOTDELEG) && !error && dp == NULL) { + KASSERT(!NFSHASNFSV4N(nmp), + ("nfsrpc_createv4: result confirm")); do { ret = nfsrpc_openrpc(VFSTONFS(dvp->v_mount), dvp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len, @@ -8009,8 +8055,12 @@ nfsrpc_openlayoutrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, nfsm_strtom(nd, op->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN); NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED); *tl++ = txdr_unsigned(NFSV4OPEN_NOCREATE); - *tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL); - nfsm_strtom(nd, name, namelen); + if (NFSHASNFSV4N(nmp)) { + *tl = txdr_unsigned(NFSV4OPEN_CLAIMFH); + } else { + *tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL); + nfsm_strtom(nd, name, namelen); + } NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED); *tl = txdr_unsigned(NFSV4OP_GETATTR); NFSZERO_ATTRBIT(&attrbits); diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c index 74d6a14fc4c4..579210941802 100644 --- a/sys/fs/nfsclient/nfs_clstate.c +++ b/sys/fs/nfsclient/nfs_clstate.c @@ -4383,9 +4383,15 @@ nfscl_moveopen(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp, nfscl_newopen(clp, NULL, &owp, NULL, &op, &nop, owp->nfsow_owner, lop->nfso_fh, lop->nfso_fhlen, cred, &newone); ndp = dp; - error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, np->n_v4->n4_fhlen, - lop->nfso_fh, lop->nfso_fhlen, lop->nfso_mode, op, - NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &ndp, 0, 0, cred, p); + if (NFSHASNFSV4N(nmp)) + error = nfscl_tryopen(nmp, vp, lop->nfso_fh, lop->nfso_fhlen, + lop->nfso_fh, lop->nfso_fhlen, lop->nfso_mode, op, + NULL, 0, &ndp, 0, 0, cred, p); + else + error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, + np->n_v4->n4_fhlen, lop->nfso_fh, lop->nfso_fhlen, + lop->nfso_mode, op, NFS4NODENAME(np->n_v4), + np->n_v4->n4_namelen, &ndp, 0, 0, cred, p); if (error) { if (newone) nfscl_freeopen(op, 0, true); @@ -4476,14 +4482,16 @@ nfsrpc_reopen(struct nfsmount *nmp, u_int8_t *fhp, int fhlen, if (error) return (error); vp = NFSTOV(np); - if (np->n_v4 != NULL) { + if (NFSHASNFSV4N(nmp)) + error = nfscl_tryopen(nmp, vp, fhp, fhlen, fhp, fhlen, mode, op, + NULL, 0, dpp, 0, 0, cred, p); + else if (np->n_v4 != NULL) error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, np->n_v4->n4_fhlen, fhp, fhlen, mode, op, NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, dpp, 0, 0, cred, p); - } else { + else error = EINVAL; - } vrele(vp); return (error); } @@ -4500,18 +4508,43 @@ nfscl_tryopen(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen, int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p) { int error; + struct nfscldeleg *dp; + bool try_busted_xdr; + dp = *ndpp; do { + *ndpp = dp; /* *ndpp needs to be set for retries. */ error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, newfhlen, mode, op, name, namelen, ndpp, reclaim, delegtype, cred, p, 0, 0); + try_busted_xdr = false; if (error == NFSERR_DELAY) (void) nfs_catnap(PZERO, error, "nfstryop"); - } while (error == NFSERR_DELAY); + else if (error == NFSERR_EXPIRED && NFSHASNFSV4N(nmp) && + reclaim == 0 && dp != NULL) { + /* This case is a Claim_Deleg_Cur_FH Open. */ + NFSLOCKMNT(nmp); + if ((nmp->nm_privflag & NFSMNTP_BUGGYFBSDSRV) == 0) { + /* + * Old FreeBSD NFSv4.1/4.2 servers erroneously + * expect a stateID argument for Open + * Claim_Deleg_Cur_FH and interpret the + * Getattr reply as a stateID. This results + * in an NFSERR_EXPIRED failure. + * Setting NFSMNTP_BUGGYFBSDSRV makes the Open + * send a stateID, in violation of RFC8881. + */ + try_busted_xdr = true; + nmp->nm_privflag |= NFSMNTP_BUGGYFBSDSRV; + } + NFSUNLOCKMNT(nmp); + } + } while (error == NFSERR_DELAY || try_busted_xdr); if (error == EAUTH || error == EACCES) { /* Try again using system credentials */ newnfs_setroot(cred); do { + *ndpp = dp; /* *ndpp needs to be set for retries. */ error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, newfhlen, mode, op, name, namelen, ndpp, reclaim, delegtype, cred, p, 1, 0); diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c index fd88531789d9..00b6c7617101 100644 --- a/sys/fs/nfsclient/nfs_clvnops.c +++ b/sys/fs/nfsclient/nfs_clvnops.c @@ -2052,14 +2052,6 @@ nfs_rename(struct vop_rename_args *ap) tdnp->n_fhp->nfh_len != fnp->n_v4->n4_fhlen || NFSBCMP(tdnp->n_fhp->nfh_fh, fnp->n_v4->n4_data, tdnp->n_fhp->nfh_len))) { -#ifdef notdef -{ char nnn[100]; int nnnl; -nnnl = (tcnp->cn_namelen < 100) ? tcnp->cn_namelen : 99; -bcopy(tcnp->cn_nameptr, nnn, nnnl); -nnn[nnnl] = '\0'; -printf("ren replace=%s\n",nnn); -} -#endif free(fnp->n_v4, M_NFSV4NODE); fnp->n_v4 = newv4; newv4 = NULL; @@ -2713,14 +2705,6 @@ nfs_lookitup(struct vnode *dvp, char *name, int len, struct ucred *cred, dnp->n_fhp->nfh_len != np->n_v4->n4_fhlen || NFSBCMP(dnp->n_fhp->nfh_fh, np->n_v4->n4_data, dnp->n_fhp->nfh_len))) { -#ifdef notdef -{ char nnn[100]; int nnnl; -nnnl = (len < 100) ? len : 99; -bcopy(name, nnn, nnnl); -nnn[nnnl] = '\0'; -printf("replace=%s\n",nnn); -} -#endif free(np->n_v4, M_NFSV4NODE); np->n_v4 = malloc( sizeof (struct nfsv4node) + diff --git a/sys/fs/nfsclient/nfsmount.h b/sys/fs/nfsclient/nfsmount.h index 37b84a015dab..7571add27b9c 100644 --- a/sys/fs/nfsclient/nfsmount.h +++ b/sys/fs/nfsclient/nfsmount.h @@ -124,6 +124,7 @@ struct nfsmount { #define NFSMNTP_DELEGISSUED 0x00000400 #define NFSMNTP_NODEALLOCATE 0x00000800 #define NFSMNTP_FAKEROOTFH 0x00001000 +#define NFSMNTP_BUGGYFBSDSRV 0x00002000 /* New mount flags only used by the kernel via nmount(2). */ #define NFSMNT_TLS 0x00000001