The branch stable/14 has been updated by rmacklem:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=4e583d78b77ce6a3c05b79d82e4ccf2a3a390d8d

commit 4e583d78b77ce6a3c05b79d82e4ccf2a3a390d8d
Author:     Rick Macklem <rmack...@freebsd.org>
AuthorDate: 2023-10-20 23:10:25 +0000
Commit:     Rick Macklem <rmack...@freebsd.org>
CommitDate: 2023-11-21 23:55:19 +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.
    
    (cherry picked from commit 196787f79e67374527a1d528a42efa8b31acd9af)
---
 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 8fe9158384a0..5a16a28bd83e 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 383d45ba260b..63cb9eaec7b7 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

Reply via email to