X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/fe8ab488e9161c46dd9885d58fc52996dc0249ff..eee3565979933af707c711411001ba11fe406a3c:/bsd/nfs/nfs4_vnops.c diff --git a/bsd/nfs/nfs4_vnops.c b/bsd/nfs/nfs4_vnops.c index 2682d94be..a018cdaa7 100644 --- a/bsd/nfs/nfs4_vnops.c +++ b/bsd/nfs/nfs4_vnops.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2011 Apple Inc. All rights reserved. + * Copyright (c) 2006-2015 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -102,7 +102,7 @@ nfs4_access_rpc(nfsnode_t np, u_int32_t *access, int rpcflags, vfs_context_t ctx // PUTFH, ACCESS, GETATTR numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 17 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "access", numops); + nfsm_chain_add_compound_header(error, &nmreq, "access", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize); @@ -214,7 +214,7 @@ nfs4_getattr_rpc( // PUTFH, GETATTR numops = 2; nfsm_chain_build_alloc_init(error, &nmreq, 15 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "getattr", numops); + nfsm_chain_add_compound_header(error, &nmreq, "getattr", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, fhp, fhsize); @@ -271,7 +271,7 @@ nfs4_readlink_rpc(nfsnode_t np, char *buf, uint32_t *buflenp, vfs_context_t ctx) // PUTFH, GETATTR, READLINK numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 16 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "readlink", numops); + nfsm_chain_add_compound_header(error, &nmreq, "readlink", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, NFS_VER4, np->n_fhp, np->n_fhsize); @@ -341,7 +341,7 @@ nfs4_read_rpc_async( // PUTFH, READ, GETATTR numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 22 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "read", numops); + nfsm_chain_add_compound_header(error, &nmreq, "read", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize); @@ -453,7 +453,7 @@ nfs4_write_rpc_async( // PUTFH, WRITE, GETATTR numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 25 * NFSX_UNSIGNED + len); - nfsm_chain_add_compound_header(error, &nmreq, "write", numops); + nfsm_chain_add_compound_header(error, &nmreq, "write", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize); @@ -578,7 +578,7 @@ restart: // PUTFH, REMOVE, GETATTR numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 17 * NFSX_UNSIGNED + namelen); - nfsm_chain_add_compound_header(error, &nmreq, "remove", numops); + nfsm_chain_add_compound_header(error, &nmreq, "remove", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize); @@ -654,7 +654,7 @@ nfs4_rename_rpc( // PUTFH(FROM), SAVEFH, PUTFH(TO), RENAME, GETATTR(TO), RESTOREFH, GETATTR(FROM) numops = 7; nfsm_chain_build_alloc_init(error, &nmreq, 30 * NFSX_UNSIGNED + fnamelen + tnamelen); - nfsm_chain_add_compound_header(error, &nmreq, "rename", numops); + nfsm_chain_add_compound_header(error, &nmreq, "rename", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, fdnp->n_fhp, fdnp->n_fhsize); @@ -854,7 +854,7 @@ nfs4_readdir_rpc(nfsnode_t dnp, struct nfsbuf *bp, vfs_context_t ctx) // PUTFH, GETATTR, READDIR numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 26 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, tag, numops); + nfsm_chain_add_compound_header(error, &nmreq, tag, nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize); @@ -1104,7 +1104,7 @@ nfs4_lookup_rpc_async( // PUTFH, GETATTR, LOOKUP(P), GETFH, GETATTR (FH) numops = 5; nfsm_chain_build_alloc_init(error, &nmreq, 20 * NFSX_UNSIGNED + namelen); - nfsm_chain_add_compound_header(error, &nmreq, "lookup", numops); + nfsm_chain_add_compound_header(error, &nmreq, "lookup", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize); @@ -1160,6 +1160,8 @@ nfs4_lookup_rpc_async_finish( struct nfsm_chain nmrep; nmp = NFSTONMP(dnp); + if (nmp == NULL) + return (ENXIO); nfsvers = nmp->nm_vers; if ((name[0] == '.') && (name[1] == '.') && (namelen == 2)) isdotdot = 1; @@ -1255,7 +1257,7 @@ nfs4_commit_rpc( // PUTFH, COMMIT, GETATTR numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 19 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "commit", numops); + nfsm_chain_add_compound_header(error, &nmreq, "commit", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize); @@ -1325,7 +1327,7 @@ nfs4_pathconf_rpc( // PUTFH, GETATTR numops = 2; nfsm_chain_build_alloc_init(error, &nmreq, 16 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "pathconf", numops); + nfsm_chain_add_compound_header(error, &nmreq, "pathconf", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize); @@ -1540,7 +1542,7 @@ tryagain: // PUTFH, SETATTR, GETATTR numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 40 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "setattr", numops); + nfsm_chain_add_compound_header(error, &nmreq, "setattr", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize); @@ -2735,10 +2737,25 @@ restart: * So grab another open count matching the accessMode passed in. * If we already had an mmap open, prefer read/write without deny mode. * This means we may have to drop the current mmap open first. + * + * N.B. We should have an open for the mmap, because, mmap was + * called on an open descriptor, or we've created an open for read + * from reading the first page for execve. However, if we piggy + * backed on an existing NFS_OPEN_SHARE_ACCESS_READ/NFS_OPEN_SHARE_DENY_NONE + * that open may have closed. */ - if (!nofp->nof_access) { - if (accessMode != NFS_OPEN_SHARE_ACCESS_READ) { + if (!(nofp->nof_access & NFS_OPEN_SHARE_ACCESS_READ)) { + if (nofp->nof_flags & NFS_OPEN_FILE_NEEDCLOSE) { + /* We shouldn't get here. We've already open the file for execve */ + NP(np, "nfs_vnop_mmap: File already needs close access: 0x%x, cred: %d thread: %lld", + nofp->nof_access, kauth_cred_getuid(nofp->nof_owner->noo_cred), thread_tid(vfs_context_thread(ctx))); + } + /* + * mmapings for execve are just for read. Get out with EPERM if the accessMode is not ACCESS_READ + * or the access would be denied. Other accesses should have an open descriptor for the mapping. + */ + if (accessMode != NFS_OPEN_SHARE_ACCESS_READ || (accessMode & nofp->nof_deny)) { /* not asking for just read access -> fail */ error = EPERM; goto out; @@ -2795,6 +2812,29 @@ restart: denyMode = NFS_OPEN_SHARE_DENY_WRITE; else if (nofp->nof_r_drw) denyMode = NFS_OPEN_SHARE_DENY_BOTH; + } else if (nofp->nof_d_rw || nofp->nof_d_rw_dw || nofp->nof_d_rw_drw) { + /* + * This clause and the one below is to co-opt a read write access + * for a read only mmaping. We probably got here in that an + * existing rw open for an executable file already exists. + */ + delegated = 1; + accessMode = NFS_OPEN_SHARE_ACCESS_BOTH; + if (nofp->nof_d_rw) + denyMode = NFS_OPEN_SHARE_DENY_NONE; + else if (nofp->nof_d_rw_dw) + denyMode = NFS_OPEN_SHARE_DENY_WRITE; + else if (nofp->nof_d_rw_drw) + denyMode = NFS_OPEN_SHARE_DENY_BOTH; + } else if (nofp->nof_rw || nofp->nof_rw_dw || nofp->nof_rw_drw) { + delegated = 0; + accessMode = NFS_OPEN_SHARE_ACCESS_BOTH; + if (nofp->nof_rw) + denyMode = NFS_OPEN_SHARE_DENY_NONE; + else if (nofp->nof_rw_dw) + denyMode = NFS_OPEN_SHARE_DENY_WRITE; + else if (nofp->nof_rw_drw) + denyMode = NFS_OPEN_SHARE_DENY_BOTH; } else { error = EPERM; } @@ -3164,7 +3204,7 @@ nfs_file_lock_destroy(struct nfs_file_lock *nflp) FREE(nflp, M_TEMP); } else { lck_mtx_lock(&nlop->nlo_lock); - bzero(nflp, sizeof(nflp)); + bzero(nflp, sizeof(*nflp)); lck_mtx_unlock(&nlop->nlo_lock); } nfs_lock_owner_rele(nlop); @@ -3264,7 +3304,7 @@ nfs4_setlock_rpc( // PUTFH, GETATTR, LOCK numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 33 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "lock", numops); + nfsm_chain_add_compound_header(error, &nmreq, "lock", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, NFS_VER4, np->n_fhp, np->n_fhsize); @@ -3360,7 +3400,7 @@ nfs4_unlock_rpc( // PUTFH, GETATTR, LOCKU numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 26 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "unlock", numops); + nfsm_chain_add_compound_header(error, &nmreq, "unlock", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, NFS_VER4, np->n_fhp, np->n_fhsize); @@ -3434,7 +3474,7 @@ nfs4_getlock_rpc( // PUTFH, GETATTR, LOCKT numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 26 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "locktest", numops); + nfsm_chain_add_compound_header(error, &nmreq, "locktest", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, NFS_VER4, np->n_fhp, np->n_fhsize); @@ -4582,7 +4622,7 @@ nfs4_open_confirm_rpc( // PUTFH, OPEN_CONFIRM, GETATTR numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 23 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "open_confirm", numops); + nfsm_chain_add_compound_header(error, &nmreq, "open_confirm", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nmp->nm_vers, fhp, fhlen); @@ -4700,7 +4740,7 @@ again: // PUTFH, SAVEFH, OPEN(CREATE?), GETATTR(FH), RESTOREFH, GETATTR numops = 6; nfsm_chain_build_alloc_init(error, &nmreq, 53 * NFSX_UNSIGNED + cnp->cn_namelen); - nfsm_chain_add_compound_header(error, &nmreq, create ? "create" : "open", numops); + nfsm_chain_add_compound_header(error, &nmreq, create ? "create" : "open", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize); @@ -5040,6 +5080,7 @@ nfs4_claim_delegated_open_rpc( MALLOC(filename, char *, namelen+1, M_TEMP, M_WAITOK); if (!filename) { error = ENOMEM; + nfs_node_unlock(np); goto out; } snprintf(filename, namelen+1, "%s", name); @@ -5047,8 +5088,7 @@ nfs4_claim_delegated_open_rpc( nfs_node_unlock(np); if ((error = nfs_open_owner_set_busy(noop, NULL))) - return (error); - + goto out; NVATTR_INIT(&nvattr); delegation = NFS_OPEN_DELEGATE_NONE; dstateid = np->n_dstateid; @@ -5060,7 +5100,7 @@ nfs4_claim_delegated_open_rpc( // PUTFH, OPEN, GETATTR(FH) numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 48 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "open_claim_d", numops); + nfsm_chain_add_compound_header(error, &nmreq, "open_claim_d", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, VTONFS(dvp)->n_fhp, VTONFS(dvp)->n_fhsize); @@ -5266,7 +5306,7 @@ nfs4_open_reclaim_rpc( // PUTFH, OPEN, GETATTR(FH) numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 48 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "open_reclaim", numops); + nfsm_chain_add_compound_header(error, &nmreq, "open_reclaim", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize); @@ -5449,7 +5489,7 @@ nfs4_open_downgrade_rpc( // PUTFH, OPEN_DOWNGRADE, GETATTR numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 23 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "open_downgrd", numops); + nfsm_chain_add_compound_header(error, &nmreq, "open_downgrd", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize); @@ -5519,7 +5559,7 @@ nfs4_close_rpc( // PUTFH, CLOSE, GETATTR numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 23 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "close", numops); + nfsm_chain_add_compound_header(error, &nmreq, "close", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize); @@ -5989,7 +6029,7 @@ nfs4_delegreturn_rpc(struct nfsmount *nmp, u_char *fhp, int fhlen, struct nfs_st // PUTFH, DELEGRETURN numops = 2; nfsm_chain_build_alloc_init(error, &nmreq, 16 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "delegreturn", numops); + nfsm_chain_add_compound_header(error, &nmreq, "delegreturn", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nmp->nm_vers, fhp, fhlen); @@ -6065,42 +6105,76 @@ restart: nfs_open_owner_rele(noop); return (error); } - if (!nofp->nof_access) { - /* we don't have the file open, so open it for read access */ - error = nfs_mount_state_in_use_start(nmp, vfs_context_thread(ctx)); - if (error) { + /* + * Since the read path is a hot path, if we already have + * read access, lets go and try and do the read, without + * busying the mount and open file node for this open owner. + * + * N.B. This is inherently racy w.r.t. an execve using + * an already open file, in that the read at the end of + * this routine will be racing with a potential close. + * The code below ultimately has the same problem. In practice + * this does not seem to be an issue. + */ + if (nofp->nof_access & NFS_OPEN_SHARE_ACCESS_READ) { + nfs_open_owner_rele(noop); + goto do_read; + } + error = nfs_mount_state_in_use_start(nmp, vfs_context_thread(ctx)); + if (error) { + nfs_open_owner_rele(noop); + return (error); + } + /* + * If we don't have a file already open with the access we need (read) then + * we need to open one. Otherwise we just co-opt an open. We might not already + * have access because we're trying to read the first page of the + * file for execve. + */ + error = nfs_open_file_set_busy(nofp, vfs_context_thread(ctx)); + if (error) { + nfs_mount_state_in_use_end(nmp, 0); + nfs_open_owner_rele(noop); + return (error); + } + if (!(nofp->nof_access & NFS_OPEN_SHARE_ACCESS_READ)) { + /* we don't have the file open, so open it for read access if we're not denied */ + if (nofp->nof_flags & NFS_OPEN_FILE_NEEDCLOSE) { + NP(np, "nfs_vnop_read: File already needs close access: 0x%x, cred: %d thread: %lld", + nofp->nof_access, kauth_cred_getuid(nofp->nof_owner->noo_cred), thread_tid(vfs_context_thread(ctx))); + } + if (nofp->nof_deny & NFS_OPEN_SHARE_DENY_READ) { + nfs_open_file_clear_busy(nofp); + nfs_mount_state_in_use_end(nmp, 0); nfs_open_owner_rele(noop); - return (error); + return (EPERM); } if (np->n_flag & NREVOKE) { error = EIO; + nfs_open_file_clear_busy(nofp); nfs_mount_state_in_use_end(nmp, 0); nfs_open_owner_rele(noop); return (error); } - error = nfs_open_file_set_busy(nofp, vfs_context_thread(ctx)); - if (error) - nofp = NULL; - if (!error) { - if (nmp->nm_vers < NFS_VER4) { - /* NFS v2/v3 opens are always allowed - so just add it. */ - nfs_open_file_add_open(nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_NONE, 0); - } else { - error = nfs4_open(np, nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_NONE, ctx); - } + if (nmp->nm_vers < NFS_VER4) { + /* NFS v2/v3 opens are always allowed - so just add it. */ + nfs_open_file_add_open(nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_NONE, 0); + } else { + error = nfs4_open(np, nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_NONE, ctx); } if (!error) nofp->nof_flags |= NFS_OPEN_FILE_NEEDCLOSE; - if (nofp) - nfs_open_file_clear_busy(nofp); - if (nfs_mount_state_in_use_end(nmp, error)) { - nofp = NULL; - goto restart; - } + } + if (nofp) + nfs_open_file_clear_busy(nofp); + if (nfs_mount_state_in_use_end(nmp, error)) { + nofp = NULL; + goto restart; } nfs_open_owner_rele(noop); if (error) return (error); +do_read: return (nfs_bioread(VTONFS(ap->a_vp), ap->a_uio, ap->a_ioflag, ap->a_context)); } @@ -6348,7 +6422,7 @@ nfs4_create_rpc( // PUTFH, SAVEFH, CREATE, GETATTR(FH), RESTOREFH, GETATTR numops = 6; nfsm_chain_build_alloc_init(error, &nmreq, 66 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, tag, numops); + nfsm_chain_add_compound_header(error, &nmreq, tag, nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, dnp->n_fhp, dnp->n_fhsize); @@ -6606,7 +6680,7 @@ nfs4_vnop_link( // PUTFH(SOURCE), SAVEFH, PUTFH(DIR), LINK, GETATTR(DIR), RESTOREFH, GETATTR numops = 7; nfsm_chain_build_alloc_init(error, &nmreq, 29 * NFSX_UNSIGNED + cnp->cn_namelen); - nfsm_chain_add_compound_header(error, &nmreq, "link", numops); + nfsm_chain_add_compound_header(error, &nmreq, "link", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize); @@ -6811,7 +6885,7 @@ nfs4_named_attr_dir_get(nfsnode_t np, int fetch, vfs_context_t ctx) // PUTFH, OPENATTR, GETATTR numops = 3; nfsm_chain_build_alloc_init(error, &nmreq, 22 * NFSX_UNSIGNED); - nfsm_chain_add_compound_header(error, &nmreq, "openattr", numops); + nfsm_chain_add_compound_header(error, &nmreq, "openattr", nmp->nm_minor_vers, numops); numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH); nfsm_chain_add_fh(error, &nmreq, nmp->nm_vers, np->n_fhp, np->n_fhsize); @@ -7141,7 +7215,7 @@ restart: if (prefetch) numops += 4; // also sending: SAVEFH, RESTOREFH, NVERIFY, READ nfsm_chain_build_alloc_init(error, &nmreq, 64 * NFSX_UNSIGNED + cnp->cn_namelen); - nfsm_chain_add_compound_header(error, &nmreq, "getnamedattr", numops); + nfsm_chain_add_compound_header(error, &nmreq, "getnamedattr", nmp->nm_minor_vers, numops); if (hadattrdir) { numops--; nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);