diff options
| author | Vitaliy Gusev <gusev.vitaliy@gmail.com> | 2020-04-12 11:28:51 +0300 |
|---|---|---|
| committer | Dan McDonald <danmcd@joyent.com> | 2021-04-22 10:41:50 -0400 |
| commit | 6f57c9527c4e1e26e4dfb190459023ee084c1f52 (patch) | |
| tree | 2d0f78548f2c0de66d9e704d8c804edfc72fcd9c /usr/src/uts/common/fs | |
| parent | 043819558530b9580af0efc07bc0af452bcfcef5 (diff) | |
| download | illumos-joyent-6f57c9527c4e1e26e4dfb190459023ee084c1f52.tar.gz | |
preview of upstream nfsv4.x server support
Diffstat (limited to 'usr/src/uts/common/fs')
| -rw-r--r-- | usr/src/uts/common/fs/nfs/nfs4_attr.c | 3 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/nfs/nfs4_dispatch.c | 100 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/nfs/nfs4_srv.c | 422 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/nfs/nfs4_srv_attr.c | 62 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/nfs/nfs4_state.c | 170 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/nfs/nfs4_xdr.c | 227 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/nfs/nfs4x_dispatch.c | 177 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/nfs/nfs4x_srv.c | 1250 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/nfs/nfs4x_state.c | 576 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/nfs/nfs4x_xdr.c | 2935 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/nfs/nfs_server.c | 5 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/nfs/nfs_stats.c | 21 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/nfs/nfs_subr.c | 10 |
13 files changed, 5668 insertions, 290 deletions
diff --git a/usr/src/uts/common/fs/nfs/nfs4_attr.c b/usr/src/uts/common/fs/nfs/nfs4_attr.c index c32a9526ec..820b804312 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_attr.c +++ b/usr/src/uts/common/fs/nfs/nfs4_attr.c @@ -772,6 +772,9 @@ struct nfs4_ntov_map nfs4_ntov_map[] = { xdr_u_longlong_t, NULL, "fattr4_mounted_on_fileid" }, + { FATTR4_SUPPATTR_EXCLCREAT_MASK_LOCAL, 0, FALSE, FALSE, + FATTR4_MOUNTED_ON_FILEID + 1, 0, xdr_bitmap4, + NULL, "fattr4_suppattr_exclcreat" }, }; uint_t nfs4_ntov_map_size = sizeof (nfs4_ntov_map) / diff --git a/usr/src/uts/common/fs/nfs/nfs4_dispatch.c b/usr/src/uts/common/fs/nfs/nfs4_dispatch.c index 1fdfd0f601..0988dea0ef 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_dispatch.c +++ b/usr/src/uts/common/fs/nfs/nfs4_dispatch.c @@ -26,6 +26,7 @@ /* * Copyright 2018 Nexenta Systems, Inc. + * Copyright 2020 RackTop Systems, Inc. */ #include <sys/systm.h> @@ -40,7 +41,7 @@ #include <nfs/nfs_dispatch.h> #include <nfs/nfs4_drc.h> -#define NFS4_MAX_MINOR_VERSION 0 +#define NFS4_MAX_MINOR_VERSION 2 /* * The default size of the duplicate request cache @@ -372,43 +373,28 @@ rfs4_find_dr(struct svc_req *req, rfs4_drc_t *drc, rfs4_dupreq_t **dup) * */ int -rfs4_dispatch(struct rpcdisp *disp, struct svc_req *req, SVCXPRT *xprt, - char *ap) +rfs40_dispatch(struct svc_req *req, SVCXPRT *xprt, char *ap) { COMPOUND4res res_buf; COMPOUND4res *rbp; COMPOUND4args *cap; - cred_t *cr = NULL; int error = 0; int dis_flags = 0; int dr_stat = NFS4_NOT_DUP; rfs4_dupreq_t *drp = NULL; int rv; + struct compound_state cs; nfs4_srv_t *nsrv4 = nfs4_get_srv(); rfs4_drc_t *nfs4_drc = nsrv4->nfs4_drc; - ASSERT(disp); - - /* - * Short circuit the RPC_NULL proc. - */ - if (disp->dis_proc == rpc_null) { - DTRACE_NFSV4_1(null__start, struct svc_req *, req); - if (!svc_sendreply(xprt, xdr_void, NULL)) { - DTRACE_NFSV4_1(null__done, struct svc_req *, req); - svcerr_systemerr(xprt); - return (1); - } - DTRACE_NFSV4_1(null__done, struct svc_req *, req); - return (0); - } - /* Only NFSv4 Compounds from this point onward */ rbp = &res_buf; cap = (COMPOUND4args *)ap; + rfs4_init_compound_state(&cs); + /* * Figure out the disposition of the whole COMPOUND * and record it's IDEMPOTENTCY. @@ -445,9 +431,11 @@ rfs4_dispatch(struct rpcdisp *disp, struct svc_req *req, SVCXPRT *xprt, case NFS4_DUP_NEW: curthread->t_flag |= T_DONTPEND; /* NON-IDEMPOTENT proc call */ - rfs4_compound(cap, rbp, NULL, req, cr, &rv); + rfs4_compound(cap, rbp, &cs, req, &rv); curthread->t_flag &= ~T_DONTPEND; + rfs4_fini_compound_state(&cs); + if (rv) /* short ckt sendreply on error */ return (rv); @@ -478,9 +466,11 @@ rfs4_dispatch(struct rpcdisp *disp, struct svc_req *req, SVCXPRT *xprt, } else { curthread->t_flag |= T_DONTPEND; /* IDEMPOTENT proc call */ - rfs4_compound(cap, rbp, NULL, req, cr, &rv); + rfs4_compound(cap, rbp, &cs, req, &rv); curthread->t_flag &= ~T_DONTPEND; + rfs4_fini_compound_state(&cs); + if (rv) /* short ckt sendreply on error */ return (rv); @@ -528,19 +518,11 @@ rfs4_dispatch(struct rpcdisp *disp, struct svc_req *req, SVCXPRT *xprt, return (error); } -bool_t -rfs4_minorvers_mismatch(struct svc_req *req, SVCXPRT *xprt, void *args) +static int +rfs4_send_minor_mismatch(SVCXPRT *xprt, COMPOUND4args *argsp) { - COMPOUND4args *argsp; COMPOUND4res res_buf, *resp; - - if (req->rq_vers != 4) - return (FALSE); - - argsp = (COMPOUND4args *)args; - - if (argsp->minorversion <= NFS4_MAX_MINOR_VERSION) - return (FALSE); + int err = 0; resp = &res_buf; @@ -563,8 +545,26 @@ rfs4_minorvers_mismatch(struct svc_req *req, SVCXPRT *xprt, void *args) DTRACE_PROBE2(nfss__e__minorvers_mismatch, SVCXPRT *, xprt, char *, resp); svcerr_systemerr(xprt); + err = 1; } rfs4_compound_free(resp); + return (err); +} + +bool_t +rfs4_minorvers_mismatch(struct svc_req *req, SVCXPRT *xprt, void *args) +{ + COMPOUND4args *argsp; + + if (req->rq_vers != 4) + return (FALSE); + + argsp = (COMPOUND4args *)args; + + if (argsp->minorversion <= NFS4_MAX_MINOR_VERSION) + return (FALSE); + + (void) rfs4_send_minor_mismatch(xprt, argsp); return (TRUE); } @@ -619,3 +619,37 @@ rfs4_resource_err(struct svc_req *req, COMPOUND4args *argsp) UTF8STRING_FREE(rbp->tag); kmem_free(rbp->array, rbp->array_len * sizeof (nfs_resop4)); } + +/* ARGSUSED */ +int +rfs4_dispatch(struct rpcdisp *disp, struct svc_req *req, + SVCXPRT *xprt, char *ap) +{ + COMPOUND4args *cmp; + int error = 0; + + /* + * Handle the NULL Proc here + */ + if (req->rq_proc == RFS_NULL) { + return (!svc_sendreply(xprt, xdr_void, NULL)); + } + + cmp = (COMPOUND4args *)ap; + ASSERT(cmp != NULL); + + switch (cmp->minorversion) { + case 1: + case 2: + error = rfs4x_dispatch(req, xprt, ap); + break; + + case 0: + error = rfs40_dispatch(req, xprt, ap); + break; + + default: + error = rfs4_send_minor_mismatch(xprt, cmp); + } + return (error); +} diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv.c b/usr/src/uts/common/fs/nfs/nfs4_srv.c index 757964eb84..acaf6a8f8b 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_srv.c +++ b/usr/src/uts/common/fs/nfs/nfs4_srv.c @@ -32,6 +32,7 @@ * Copyright (c) 2012, 2016 by Delphix. All rights reserved. * Copyright 2019 Nexenta Systems, Inc. * Copyright 2019 Nexenta by DDN, Inc. + * Copyright 2020 RackTop Systems, Inc. */ #include <sys/param.h> @@ -185,6 +186,8 @@ static void rfs4_op_getfh(nfs_argop4 *, nfs_resop4 *, struct svc_req *, static void rfs4_op_getfh_free(nfs_resop4 *); static void rfs4_op_illegal(nfs_argop4 *, nfs_resop4 *, struct svc_req *, struct compound_state *); +static void rfs4_op_notsup(nfs_argop4 *, nfs_resop4 *, struct svc_req *, + struct compound_state *); static void rfs4_op_link(nfs_argop4 *, nfs_resop4 *, struct svc_req *, struct compound_state *); static void rfs4_op_lock(nfs_argop4 *, nfs_resop4 *, struct svc_req *, @@ -247,12 +250,36 @@ static void rfs4_op_secinfo(nfs_argop4 *, nfs_resop4 *, struct svc_req *, struct compound_state *); static void rfs4_op_secinfo_free(nfs_resop4 *); +void rfs4x_op_exchange_id(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, struct compound_state *cs); +void rfs4x_exchange_id_free(nfs_resop4 *); + +void rfs4x_op_create_session(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, struct compound_state *cs); + +void rfs4x_op_destroy_session(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, compound_state_t *cs); + +void rfs4x_op_sequence(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, struct compound_state *cs); + +void rfs4x_op_reclaim_complete(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, compound_state_t *cs); + +void rfs4x_op_destroy_clientid(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, compound_state_t *cs); + +void rfs4x_op_bind_conn_to_session(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, compound_state_t *cs); + +void rfs4x_op_secinfo_noname(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, compound_state_t *cs); + static nfsstat4 check_open_access(uint32_t, struct compound_state *, struct svc_req *); nfsstat4 rfs4_client_sysid(rfs4_client_t *, sysid_t *); void rfs4_ss_clid(nfs4_srv_t *, rfs4_client_t *); - /* * translation table for attrs */ @@ -410,6 +437,67 @@ static struct rfsv4disp rfsv4disptab[] = { /* OP_RELEASE_LOCKOWNER = 39 */ {rfs4_op_release_lockowner, nullfree, 0}, + + /* + * NFSv4.1 operations + */ + + /* OP_BACKCHANNEL_CTL = 40 */ + {rfs4_op_notsup, nullfree, 0}, + + /* OP_BIND_CONN_TO_SESSION = 41 */ + {rfs4x_op_bind_conn_to_session, nullfree, 0}, + + /* OP_EXCHANGE_ID = 42 */ + {rfs4x_op_exchange_id, rfs4x_exchange_id_free, 0}, + + /* OP_CREATE_SESSION = 43 */ + {rfs4x_op_create_session, nullfree, 0}, + + /* OP_DESTROY_SESSION = 44 */ + {rfs4x_op_destroy_session, nullfree, 0}, + + /* OP_FREE_STATEID = 45 */ + {rfs4_op_notsup, nullfree, 0}, + + /* OP_GET_DIR_DELEGATION = 46 */ + {rfs4_op_notsup, nullfree, 0}, + + /* OP_GETDEVICEINFO = 47 */ + {rfs4_op_notsup, nullfree, 0}, + + /* OP_GETDEVICELIST = 48 */ + {rfs4_op_notsup, nullfree, 0}, + + /* OP_LAYOUTCOMMIT = 49 */ + {rfs4_op_notsup, nullfree, 0}, + + /* OP_LAYOUTGET = 50 */ + {rfs4_op_notsup, nullfree, 0}, + + /* OP_LAYOUTRETURN = 51 */ + {rfs4_op_notsup, nullfree, 0}, + + /* OP_SECINFO_NO_NAME = 52 */ + {rfs4x_op_secinfo_noname, rfs4_op_secinfo_free, 0}, + + /* OP_SEQUENCE = 53 */ + {rfs4x_op_sequence, nullfree, 0}, + + /* OP_SET_SSV = 54 */ + {rfs4_op_notsup, nullfree, 0}, + + /* OP_TEST_STATEID = 55 */ + {rfs4_op_notsup, nullfree, 0}, + + /* OP_WANT_DELEGATION = 56 */ + {rfs4_op_notsup, nullfree, 0}, + + /* OP_DESTROY_CLIENTID = 57 */ + {rfs4x_op_destroy_clientid, nullfree, 0}, + + /* OP_RECLAIM_COMPLETE = 58 */ + {rfs4x_op_reclaim_complete, nullfree, 0}, }; static uint_t rfsv4disp_cnt = sizeof (rfsv4disptab) / sizeof (rfsv4disptab[0]); @@ -463,8 +551,29 @@ static char *rfs4_op_string[] = { "rfs4_op_verify", "rfs4_op_write", "rfs4_op_release_lockowner", + /* NFSv4.1 */ + "backchannel_ctl", + "bind_conn_to_session", + "exchange_id", + "create_session", + "destroy_session", + "free_stateid", + "get_dir_delegation", + "getdeviceinfo", + "getdevicelist", + "layoutcommit", + "layoutget", + "layoutreturn", + "secinfo_no_name", + "sequence", + "set_ssv", + "test_stateid", + "want_delegation", + "destroy_clientid", + "reclaim_complete", "rfs4_op_illegal" }; + #endif void rfs4_ss_chkclid(nfs4_srv_t *, rfs4_client_t *); @@ -662,6 +771,31 @@ rfs4_init_compound_state(struct compound_state *cs) cs->fh.nfs_fh4_val = cs->fhbuf; } +/* Do cleanup of the compound_state */ +void +rfs4_fini_compound_state(struct compound_state *cs) +{ + if (cs->vp) { + VN_RELE(cs->vp); + } + if (cs->saved_vp) { + VN_RELE(cs->saved_vp); + } + if (cs->cr) { + crfree(cs->cr); + } + if (cs->saved_fh.nfs_fh4_val) { + kmem_free(cs->saved_fh.nfs_fh4_val, NFS4_FHSIZE); + } + + if (cs->basecr) { + crfree(cs->basecr); + } + if (cs->sp) { + rfs4x_session_rele(cs->sp); + } +} + void rfs4_grace_start(rfs4_servinst_t *sip) { @@ -697,6 +831,8 @@ rfs4_servinst_in_grace(rfs4_servinst_t *sip) rw_enter(&sip->rwlock, RW_READER); grace_expiry = sip->start_time + sip->grace_period; + if (sip->nreclaim == 0) + grace_expiry = 0; rw_exit(&sip->rwlock); return (((time_t)TICK_TO_SEC(ddi_get_lbolt())) < grace_expiry); @@ -796,6 +932,7 @@ rfs4_servinst_create(nfs4_srv_t *nsrv4, int start_grace, sip = kmem_alloc(sizeof (rfs4_servinst_t), KM_SLEEP); rw_init(&sip->rwlock, NULL, RW_DEFAULT, NULL); + sip->nreclaim = 0; sip->start_time = (time_t)0; sip->grace_period = (time_t)0; sip->next = NULL; @@ -948,7 +1085,7 @@ in_flavor_list(int nfsnum, int *flavor_list, int count) * export structure associated with the component. */ /* ARGSUSED */ -static nfsstat4 +nfsstat4 do_rfs4_op_secinfo(struct compound_state *cs, char *nm, SECINFO4res *resp) { int error, different_export = 0; @@ -1274,9 +1411,14 @@ rfs4_op_secinfo(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, goto out; } - *cs->statusp = resp->status = do_rfs4_op_secinfo(cs, name, resp); + if (resp->status == NFS4_OK && rfs4_has_session(cs)) { + /* See rfc 5661 section 2.6.3.1.1.8 and 18.29.3 */ + VN_RELE(cs->vp); + cs->vp = NULL; + } + if (name != nm) kmem_free(name, MAXPATHLEN + 1); kmem_free(nm, len); @@ -2521,6 +2663,14 @@ rfs4_op_illegal(nfs_argop4 *argop, nfs_resop4 *resop, *cs->statusp = resp->status = NFS4ERR_OP_ILLEGAL; } +/* ARGSUSED */ +static void +rfs4_op_notsup(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, + struct compound_state *cs) +{ + *cs->statusp = *((nfsstat4 *)&(resop)->nfs_resop4_u) = NFS4ERR_NOTSUPP; +} + /* * link: args: SAVED_FH: file, CURRENT_FH: target directory * res: status. If success - CURRENT_FH unchanged, return change_info @@ -3257,7 +3407,7 @@ rfs4_op_read(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, } if ((stat = rfs4_check_stateid(FREAD, vp, &args->stateid, FALSE, - deleg, TRUE, &ct)) != NFS4_OK) { + deleg, TRUE, &ct, cs)) != NFS4_OK) { *cs->statusp = resp->status = stat; goto out; } @@ -5198,6 +5348,7 @@ do_rfs4_op_setattr(bitmap4 *resp, fattr4 *fattrp, struct compound_state *cs, */ goto done; } + if ((sarg.vap->va_mask == 0) && (! (fattrp->attrmask & FATTR4_ACL_MASK))) { /* @@ -5233,7 +5384,7 @@ do_rfs4_op_setattr(bitmap4 *resp, fattr4 *fattrp, struct compound_state *cs, if (sarg.vap->va_mask & AT_SIZE) { trunc = (sarg.vap->va_size == 0); status = rfs4_check_stateid(FWRITE, cs->vp, stateid, - trunc, &cs->deleg, sarg.vap->va_mask & AT_SIZE, &ct); + trunc, &cs->deleg, sarg.vap->va_mask & AT_SIZE, &ct, cs); if (status != NFS4_OK) goto done; } else { @@ -5622,7 +5773,7 @@ rfs4_op_write(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, cr = cs->cr; if ((stat = rfs4_check_stateid(FWRITE, vp, &args->stateid, FALSE, - deleg, TRUE, &ct)) != NFS4_OK) { + deleg, TRUE, &ct, cs)) != NFS4_OK) { *cs->statusp = resp->status = stat; goto out; } @@ -5790,18 +5941,31 @@ out: /* XXX put in a header file */ extern int sec_svc_getcred(struct svc_req *, cred_t *, caddr_t *, int *); +static inline int +rfs4_opnum_in_range(const compound_state_t *cs, int opnum) +{ + if (opnum < FIRST_NFS4_OP || opnum > LAST_NFS4_OP) + return (0); + else if (cs->minorversion == 0 && opnum > LAST_NFS40_OP) + return (0); + else if (cs->minorversion == 1 && opnum > LAST_NFS41_OP) + return (0); + else if (cs->minorversion == 2 && opnum > LAST_NFS42_OP) + return (0); + return (1); +} + void -rfs4_compound(COMPOUND4args *args, COMPOUND4res *resp, struct exportinfo *exi, - struct svc_req *req, cred_t *cr, int *rv) +rfs4_compound(COMPOUND4args *args, COMPOUND4res *resp, compound_state_t *cs, + struct svc_req *req, int *rv) { uint_t i; - struct compound_state cs; + cred_t *cr; nfs4_srv_t *nsrv4; nfs_export_t *ne = nfs_get_export(); if (rv != NULL) *rv = 0; - rfs4_init_compound_state(&cs); /* * Form a reply tag by copying over the request tag. */ @@ -5815,53 +5979,41 @@ rfs4_compound(COMPOUND4args *args, COMPOUND4res *resp, struct exportinfo *exi, resp->tag.utf8string_val = NULL; } - cs.statusp = &resp->status; - cs.req = req; + cs->statusp = &resp->status; + cs->req = req; + cs->minorversion = args->minorversion; resp->array = NULL; resp->array_len = 0; - /* - * XXX for now, minorversion should be zero - */ - if (args->minorversion != NFS4_MINORVERSION) { - DTRACE_NFSV4_2(compound__start, struct compound_state *, - &cs, COMPOUND4args *, args); - resp->status = NFS4ERR_MINOR_VERS_MISMATCH; - DTRACE_NFSV4_2(compound__done, struct compound_state *, - &cs, COMPOUND4res *, resp); - return; - } - if (args->array_len == 0) { resp->status = NFS4_OK; return; } - ASSERT(exi == NULL); - ASSERT(cr == NULL); - cr = crget(); ASSERT(cr != NULL); - if (sec_svc_getcred(req, cr, &cs.principal, &cs.nfsflavor) == 0) { + if (sec_svc_getcred(req, cr, &cs->principal, &cs->nfsflavor) == 0) { DTRACE_NFSV4_2(compound__start, struct compound_state *, - &cs, COMPOUND4args *, args); + cs, COMPOUND4args *, args); crfree(cr); DTRACE_NFSV4_2(compound__done, struct compound_state *, - &cs, COMPOUND4res *, resp); + cs, COMPOUND4res *, resp); svcerr_badcred(req->rq_xprt); if (rv != NULL) *rv = 1; return; } + resp->array_len = args->array_len; resp->array = kmem_zalloc(args->array_len * sizeof (nfs_resop4), KM_SLEEP); - cs.basecr = cr; + cs->op_len = args->array_len; + cs->basecr = cr; nsrv4 = nfs4_get_srv(); - DTRACE_NFSV4_2(compound__start, struct compound_state *, &cs, + DTRACE_NFSV4_2(compound__start, struct compound_state *, cs, COMPOUND4args *, args); /* @@ -5890,7 +6042,7 @@ rfs4_compound(COMPOUND4args *args, COMPOUND4res *resp, struct exportinfo *exi, nsrv4->seen_first_compound = 1; } - for (i = 0; i < args->array_len && cs.cont; i++) { + for (i = 0; i < args->array_len && cs->cont; i++) { nfs_argop4 *argop; nfs_resop4 *resop; uint_t op; @@ -5901,7 +6053,8 @@ rfs4_compound(COMPOUND4args *args, COMPOUND4res *resp, struct exportinfo *exi, resop->resop = argop->argop; op = (uint_t)resop->resop; - if (op < rfsv4disp_cnt) { + cs->op_pos = i; + if (op < rfsv4disp_cnt && rfs4_opnum_in_range(cs, op)) { /* * Count the individual ops here; NULL and COMPOUND * are counted in common_dispatch() @@ -5910,11 +6063,11 @@ rfs4_compound(COMPOUND4args *args, COMPOUND4res *resp, struct exportinfo *exi, NFS4_DEBUG(rfs4_debug > 1, (CE_NOTE, "Executing %s", rfs4_op_string[op])); - (*rfsv4disptab[op].dis_proc)(argop, resop, req, &cs); + (*rfsv4disptab[op].dis_proc)(argop, resop, req, cs); NFS4_DEBUG(rfs4_debug > 1, (CE_NOTE, "%s returned %d", - rfs4_op_string[op], *cs.statusp)); - if (*cs.statusp != NFS4_OK) - cs.cont = FALSE; + rfs4_op_string[op], *cs->statusp)); + if (*cs->statusp != NFS4_OK) + cs->cont = FALSE; } else { /* * This is effectively dead code since XDR code @@ -5925,15 +6078,15 @@ rfs4_compound(COMPOUND4args *args, COMPOUND4res *resp, struct exportinfo *exi, op = OP_ILLEGAL; stat[OP_ILLEGAL_IDX].value.ui64++; - rfs4_op_illegal(argop, resop, req, &cs); - cs.cont = FALSE; + rfs4_op_illegal(argop, resop, req, cs); + cs->cont = FALSE; } /* * If not at last op, and if we are to stop, then * compact the results array. */ - if ((i + 1) < args->array_len && !cs.cont) { + if ((i + 1) < args->array_len && !cs->cont) { nfs_resop4 *new_res = kmem_alloc( (i+1) * sizeof (nfs_resop4), KM_SLEEP); bcopy(resp->array, @@ -5948,27 +6101,9 @@ rfs4_compound(COMPOUND4args *args, COMPOUND4res *resp, struct exportinfo *exi, rw_exit(&ne->exported_lock); - /* - * clear exportinfo and vnode fields from compound_state before dtrace - * probe, to avoid tracing residual values for path and share path. - */ - if (cs.vp) - VN_RELE(cs.vp); - if (cs.saved_vp) - VN_RELE(cs.saved_vp); - cs.exi = cs.saved_exi = NULL; - cs.vp = cs.saved_vp = NULL; - - DTRACE_NFSV4_2(compound__done, struct compound_state *, &cs, + DTRACE_NFSV4_2(compound__done, struct compound_state *, cs, COMPOUND4res *, resp); - if (cs.saved_fh.nfs_fh4_val) - kmem_free(cs.saved_fh.nfs_fh4_val, NFS4_FHSIZE); - - if (cs.basecr) - crfree(cs.basecr); - if (cs.cr) - crfree(cs.cr); /* * done with this compound request, free the label */ @@ -6094,7 +6229,7 @@ static void lock_print(char *str, int operation, struct flock64 *flk) /*ARGSUSED*/ static bool_t -creds_ok(cred_set_t cr_set, struct svc_req *req, struct compound_state *cs) +creds_ok(cred_set_t *cr_set, struct svc_req *req, struct compound_state *cs) { return (TRUE); } @@ -6360,6 +6495,21 @@ check_open_access(uint32_t access, struct compound_state *cs, return (NFS4_OK); } +static void +rfs4_verifier_to_mtime(verifier4 v, timestruc_t *mtime) +{ + timespec32_t *time = (timespec32_t *)&v; + + /* + * Ensure no time overflows. Assumes underlying + * filesystem supports at least 32 bits. + * Truncate nsec to usec resolution to allow valid + * compares even if the underlying filesystem truncates. + */ + mtime->tv_sec = time->tv_sec % TIME32_MAX; + mtime->tv_nsec = (time->tv_nsec / 1000) * 1000; +} + static nfsstat4 rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs, change_info4 *cinfo, bitmap4 *attrset, clientid4 clientid) @@ -6373,7 +6523,6 @@ rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs, vnode_t *vp; vattr_t bva, ava, iva, cva, *vap; vnode_t *dvp; - timespec32_t *mtime; char *nm = NULL; uint_t buflen; bool_t created; @@ -6386,6 +6535,9 @@ rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs, bslabel_t *clabel; struct sockaddr *ca; char *name = NULL; + fattr4 *fattr = NULL; + + ASSERT(*attrset == 0); sarg.sbp = &sb; sarg.is_referral = B_FALSE; @@ -6411,11 +6563,17 @@ rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs, } } + if ((args->mode == EXCLUSIVE4 || args->mode == EXCLUSIVE4_1) && + dvp->v_flag & V_XATTRDIR) { + /* prohibit EXCL create of named attributes */ + return (NFS4ERR_INVAL); + } + /* * Get the last component of path name in nm. cs will reference * the including directory on success. */ - component = &args->open_claim4_u.file; + component = &args->claim.open_claim4_u.file; status = utf8_dir_verify(component); if (status != NFS4_OK) return (status); @@ -6448,12 +6606,17 @@ rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs, case GUARDED4: /*FALLTHROUGH*/ case UNCHECKED4: + case EXCLUSIVE4_1: nfs4_ntov_table_init(&ntov); ntov_table_init = TRUE; - *attrset = 0; + if (args->mode == EXCLUSIVE4_1) + fattr = &args->createhow4_u.ch_createboth.cva_attrs; + else + fattr = &args->createhow4_u.createattrs; + status = do_rfs4_set_attrs(attrset, - &args->createhow4_u.createattrs, + fattr, cs, &sarg, &ntov, NFS4ATTR_SETIT); if (status == NFS4_OK && (sarg.vap->va_mask & AT_TYPE) && @@ -6494,28 +6657,24 @@ rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs, } setsize = TRUE; } + if (args->mode == EXCLUSIVE4_1) { + rfs4_verifier_to_mtime( + args->createhow4_u.ch_createboth.cva_verf, + &vap->va_mtime); + /* attrset will be set later */ + fattr->attrmask |= FATTR4_TIME_MODIFY_MASK; + vap->va_mask |= AT_MTIME; + } break; case EXCLUSIVE4: - /* prohibit EXCL create of named attributes */ - if (dvp->v_flag & V_XATTRDIR) { - kmem_free(nm, buflen); - *attrset = 0; - return (NFS4ERR_INVAL); - } - cva.va_mask = AT_TYPE | AT_MTIME | AT_MODE; cva.va_type = VREG; - /* - * Ensure no time overflows. Assumes underlying - * filesystem supports at least 32 bits. - * Truncate nsec to usec resolution to allow valid - * compares even if the underlying filesystem truncates. - */ - mtime = (timespec32_t *)&args->createhow4_u.createverf; - cva.va_mtime.tv_sec = mtime->tv_sec % TIME32_MAX; - cva.va_mtime.tv_nsec = (mtime->tv_nsec / 1000) * 1000; cva.va_mode = (mode_t)0; + + rfs4_verifier_to_mtime(args->createhow4_u.createverf, + &cva.va_mtime); + vap = &cva; /* @@ -6551,7 +6710,7 @@ rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs, trunc = (setsize && !created); if (args->mode != EXCLUSIVE4) { - bitmap4 createmask = args->createhow4_u.createattrs.attrmask; + bitmap4 createmask = fattr->attrmask; /* * True verification that object was created with correct @@ -6993,6 +7152,16 @@ rfs4_do_open(struct compound_state *cs, struct svc_req *req, /*ARGSUSED*/ static void +rfs4_do_openfh(struct compound_state *cs, struct svc_req *req, OPEN4args *args, + rfs4_openowner_t *oo, OPEN4res *resp) +{ + /* cs->vp and cs->fh have been updated by putfh. */ + rfs4_do_open(cs, req, oo, DELEG_ANY, + (args->share_access & 0xff), args->share_deny, resp, 0); +} + +/*ARGSUSED*/ +static void rfs4_do_opennull(struct compound_state *cs, struct svc_req *req, OPEN4args *args, rfs4_openowner_t *oo, OPEN4res *resp) { @@ -7000,7 +7169,7 @@ rfs4_do_opennull(struct compound_state *cs, struct svc_req *req, bitmap4 *attrset = &resp->attrset; if (args->opentype == OPEN4_NOCREATE) - resp->status = rfs4_lookupfile(&args->open_claim4_u.file, + resp->status = rfs4_lookupfile(&args->claim.open_claim4_u.file, req, cs, args->share_access, cinfo); else { /* inhibit delegation grants during exclusive create */ @@ -7087,7 +7256,7 @@ rfs4_do_openprev(struct compound_state *cs, struct svc_req *req, cinfo->atomic = FALSE; rfs4_do_open(cs, req, oo, - NFS4_DELEG4TYPE2REQTYPE(args->open_claim4_u.delegate_type), + NFS4_DELEG4TYPE2REQTYPE(args->claim.open_claim4_u.delegate_type), args->share_access, args->share_deny, resp, 0); } @@ -7098,7 +7267,7 @@ rfs4_do_opendelcur(struct compound_state *cs, struct svc_req *req, int error; nfsstat4 status; stateid4 stateid = - args->open_claim4_u.delegate_cur_info.delegate_stateid; + args->claim.open_claim4_u.delegate_cur_info.delegate_stateid; rfs4_deleg_state_t *dsp; /* @@ -7168,7 +7337,8 @@ rfs4_do_opendelprev(struct compound_state *cs, struct svc_req *req, nfsace4 *ace; /* Note we ignore oflags */ - resp->status = rfs4_lookupfile(&args->open_claim4_u.file_delegate_prev, + resp->status = rfs4_lookupfile( + &args->claim.open_claim4_u.file_delegate_prev, req, cs, args->share_access, &resp->cinfo); if (resp->status != NFS4_OK) { @@ -7298,10 +7468,14 @@ rfs4_check_seqid(seqid4 seqid, nfs_resop4 *lastop, static rfs4_chkseq_t -rfs4_check_open_seqid(seqid4 seqid, rfs4_openowner_t *op, nfs_resop4 *resop) +rfs4_check_open_seqid(seqid4 seqid, rfs4_openowner_t *op, nfs_resop4 *resop, + const compound_state_t *cs) { rfs4_chkseq_t rc; + if (rfs4_has_session(cs)) + return (NFS4_CHKSEQ_OKAY); + rfs4_dbe_lock(op->ro_dbe); rc = rfs4_check_seqid(op->ro_open_seqid, &op->ro_reply, seqid, resop, TRUE); @@ -7347,7 +7521,7 @@ rfs4_op_open(nfs_argop4 *argop, nfs_resop4 *resop, OPEN4args *args = &argop->nfs_argop4_u.opopen; OPEN4res *resp = &resop->nfs_resop4_u.opopen; open_owner4 *owner = &args->owner; - open_claim_type4 claim = args->claim; + open_claim_type4 claim = args->claim.claim; rfs4_client_t *cp; rfs4_openowner_t *oo; bool_t create; @@ -7362,6 +7536,10 @@ rfs4_op_open(nfs_argop4 *argop, nfs_resop4 *resop, goto end; } + /* rfc5661 section 18.16.3 */ + if (rfs4_has_session(cs)) + owner->clientid = cs->client->rc_clientid; + /* * Need to check clientid and lease expiration first based on * error ordering and incrementing sequence id. @@ -7394,6 +7572,13 @@ retry: goto end; } + /* + * OPEN_CONFIRM must not be implemented in v4.1 + */ + if (rfs4_has_session(cs)) { + oo->ro_need_confirm = FALSE; + } + /* Hold off access to the sequence space while the open is done */ rfs4_sw_enter(&oo->ro_sw); @@ -7402,7 +7587,7 @@ retry: * the sequence id. */ if (!create && !oo->ro_postpone_confirm) { - switch (rfs4_check_open_seqid(args->seqid, oo, resop)) { + switch (rfs4_check_open_seqid(args->seqid, oo, resop, cs)) { case NFS4_CHKSEQ_BAD: if ((args->seqid > oo->ro_open_seqid) && oo->ro_need_confirm) { @@ -7434,7 +7619,8 @@ retry: } /* Grace only applies to regular-type OPENs */ if (rfs4_clnt_in_grace(cp) && - (claim == CLAIM_NULL || claim == CLAIM_DELEGATE_CUR)) { + (claim == CLAIM_NULL || claim == CLAIM_DELEGATE_CUR || + claim == CLAIM_FH)) { *cs->statusp = resp->status = NFS4ERR_GRACE; goto out; } @@ -7512,6 +7698,9 @@ retry: case CLAIM_DELEGATE_PREV: rfs4_do_opendelprev(cs, req, args, oo, resp); break; + case CLAIM_FH: + rfs4_do_openfh(cs, req, args, oo, resp); + break; default: resp->status = NFS4ERR_INVAL; break; @@ -7636,6 +7825,8 @@ rfs4_op_open_confirm(nfs_argop4 *argop, nfs_resop4 *resop, DTRACE_NFSV4_2(op__open__confirm__start, struct compound_state *, cs, OPEN_CONFIRM4args *, args); + ASSERT(!rfs4_has_session(cs)); + if (cs->vp == NULL) { *cs->statusp = resp->status = NFS4ERR_NOFILEHANDLE; goto out; @@ -7663,10 +7854,10 @@ rfs4_op_open_confirm(nfs_argop4 *argop, nfs_resop4 *resop, /* hold off other access to open_owner while we tinker */ rfs4_sw_enter(&sp->rs_owner->ro_sw); - switch (rfs4_check_stateid_seqid(sp, &args->open_stateid)) { + switch (rfs4_check_stateid_seqid(sp, &args->open_stateid, cs)) { case NFS4_CHECK_STATEID_OKAY: if (rfs4_check_open_seqid(args->seqid, sp->rs_owner, - resop) != 0) { + resop, cs) != 0) { *cs->statusp = resp->status = NFS4ERR_BAD_SEQID; break; } @@ -7692,7 +7883,7 @@ rfs4_op_open_confirm(nfs_argop4 *argop, nfs_resop4 *resop, break; case NFS4_CHECK_STATEID_REPLAY: switch (rfs4_check_open_seqid(args->seqid, sp->rs_owner, - resop)) { + resop, cs)) { case NFS4_CHKSEQ_OKAY: /* * This is replayed stateid; if seqid matches @@ -7714,7 +7905,7 @@ rfs4_op_open_confirm(nfs_argop4 *argop, nfs_resop4 *resop, break; case NFS4_CHECK_STATEID_UNCONFIRMED: if (rfs4_check_open_seqid(args->seqid, sp->rs_owner, - resop) != NFS4_CHKSEQ_OKAY) { + resop, cs) != NFS4_CHKSEQ_OKAY) { *cs->statusp = resp->status = NFS4ERR_BAD_SEQID; break; } @@ -7783,10 +7974,10 @@ rfs4_op_open_downgrade(nfs_argop4 *argop, nfs_resop4 *resop, /* hold off other access to open_owner while we tinker */ rfs4_sw_enter(&sp->rs_owner->ro_sw); - switch (rfs4_check_stateid_seqid(sp, &args->open_stateid)) { + switch (rfs4_check_stateid_seqid(sp, &args->open_stateid, cs)) { case NFS4_CHECK_STATEID_OKAY: if (rfs4_check_open_seqid(args->seqid, sp->rs_owner, - resop) != NFS4_CHKSEQ_OKAY) { + resop, cs) != NFS4_CHKSEQ_OKAY) { *cs->statusp = resp->status = NFS4ERR_BAD_SEQID; goto end; } @@ -7807,9 +7998,11 @@ rfs4_op_open_downgrade(nfs_argop4 *argop, nfs_resop4 *resop, *cs->statusp = resp->status = NFS4ERR_BAD_STATEID; goto end; case NFS4_CHECK_STATEID_REPLAY: + ASSERT(!rfs4_has_session(cs)); + /* Check the sequence id for the open owner */ switch (rfs4_check_open_seqid(args->seqid, sp->rs_owner, - resop)) { + resop, cs)) { case NFS4_CHKSEQ_OKAY: /* * This is replayed stateid; if seqid matches @@ -8098,7 +8291,7 @@ retry: */ if (cp_confirmed) { /* If creds don't match then client identifier is inuse */ - if (!creds_ok(cp_confirmed->rc_cr_set, req, cs)) { + if (!creds_ok(&cp_confirmed->rc_cr_set, req, cs)) { rfs4_cbinfo_t *cbp; /* * Some one else has established this client @@ -8260,7 +8453,7 @@ rfs4_op_setclientid_confirm(nfs_argop4 *argop, nfs_resop4 *resop, goto out; } - if (!creds_ok(cp, req, cs)) { + if (!creds_ok(&cp->rc_cr_set, req, cs)) { *cs->statusp = res->status = NFS4ERR_CLID_INUSE; rfs4_client_rele(cp); goto out; @@ -8352,10 +8545,10 @@ rfs4_op_close(nfs_argop4 *argop, nfs_resop4 *resop, /* hold off other access to open_owner while we tinker */ rfs4_sw_enter(&sp->rs_owner->ro_sw); - switch (rfs4_check_stateid_seqid(sp, &args->open_stateid)) { + switch (rfs4_check_stateid_seqid(sp, &args->open_stateid, cs)) { case NFS4_CHECK_STATEID_OKAY: if (rfs4_check_open_seqid(args->seqid, sp->rs_owner, - resop) != NFS4_CHKSEQ_OKAY) { + resop, cs) != NFS4_CHKSEQ_OKAY) { *cs->statusp = resp->status = NFS4ERR_BAD_SEQID; goto end; } @@ -8376,9 +8569,11 @@ rfs4_op_close(nfs_argop4 *argop, nfs_resop4 *resop, *cs->statusp = resp->status = NFS4ERR_BAD_STATEID; goto end; case NFS4_CHECK_STATEID_REPLAY: + ASSERT(!rfs4_has_session(cs)); + /* Check the sequence id for the open owner */ switch (rfs4_check_open_seqid(args->seqid, sp->rs_owner, - resop)) { + resop, cs)) { case NFS4_CHKSEQ_OKAY: /* * This is replayed stateid; if seqid matches @@ -8911,7 +9106,7 @@ rfs4_op_lock(nfs_argop4 *argop, nfs_resop4 *resop, /* hold off other access to open_owner while we tinker */ rfs4_sw_enter(&sp->rs_owner->ro_sw); - switch (rc = rfs4_check_stateid_seqid(sp, stateid)) { + switch (rc = rfs4_check_stateid_seqid(sp, stateid, cs)) { case NFS4_CHECK_STATEID_OLD: *cs->statusp = resp->status = NFS4ERR_OLD_STATEID; goto end; @@ -8928,7 +9123,12 @@ rfs4_op_lock(nfs_argop4 *argop, nfs_resop4 *resop, *cs->statusp = resp->status = NFS4ERR_OLD_STATEID; goto end; case NFS4_CHECK_STATEID_OKAY: + if (rfs4_has_session(cs)) + break; + /* FALLTHROUGH */ case NFS4_CHECK_STATEID_REPLAY: + ASSERT(!rfs4_has_session(cs)); + switch (rfs4_check_olo_seqid(olo->open_seqid, sp->rs_owner, resop)) { case NFS4_CHKSEQ_OKAY: @@ -8995,7 +9195,7 @@ rfs4_op_lock(nfs_argop4 *argop, nfs_resop4 *resop, * not appropriate. The client should be using the * existing lock_owner branch. */ - if (dup_lock == FALSE && create == FALSE) { + if (!rfs4_has_session(cs) && !dup_lock && !create) { if (lsp->rls_lock_completed == TRUE) { *cs->statusp = resp->status = NFS4ERR_BAD_SEQID; @@ -9087,12 +9287,15 @@ rfs4_op_lock(nfs_argop4 *argop, nfs_resop4 *resop, rfs4_sw_enter(&lsp->rls_sw); ls_sw_held = TRUE; - switch (rfs4_check_lo_stateid_seqid(lsp, stateid)) { + switch (rfs4_check_lo_stateid_seqid(lsp, stateid, cs)) { /* * The stateid looks like it was okay (expected to be * the next one) */ case NFS4_CHECK_STATEID_OKAY: + if (rfs4_has_session(cs)) + break; + /* * The sequence id is now checked. Determine * if this is a replay or if it is in the @@ -9152,6 +9355,8 @@ rfs4_op_lock(nfs_argop4 *argop, nfs_resop4 *resop, *cs->statusp = resp->status = NFS4ERR_OLD_STATEID; goto end; case NFS4_CHECK_STATEID_REPLAY: + ASSERT(!rfs4_has_session(cs)); + switch (rfs4_check_lock_seqid( args->locker.locker4_u.lock_owner.lock_seqid, lsp, resop)) { @@ -9317,8 +9522,11 @@ rfs4_op_locku(nfs_argop4 *argop, nfs_resop4 *resop, /* hold off other access to lsp while we tinker */ rfs4_sw_enter(&lsp->rls_sw); - switch (rfs4_check_lo_stateid_seqid(lsp, stateid)) { + switch (rfs4_check_lo_stateid_seqid(lsp, stateid, cs)) { case NFS4_CHECK_STATEID_OKAY: + if (rfs4_has_session(cs)) + break; + if (rfs4_check_lock_seqid(args->seqid, lsp, resop) != NFS4_CHKSEQ_OKAY) { *cs->statusp = resp->status = NFS4ERR_BAD_SEQID; @@ -9338,6 +9546,8 @@ rfs4_op_locku(nfs_argop4 *argop, nfs_resop4 *resop, *cs->statusp = resp->status = NFS4ERR_OLD_STATEID; goto end; case NFS4_CHECK_STATEID_REPLAY: + ASSERT(!rfs4_has_session(cs)); + switch (rfs4_check_lock_seqid(args->seqid, lsp, resop)) { case NFS4_CHKSEQ_OKAY: /* diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c b/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c index a9ee217a8b..a558c48466 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c +++ b/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c @@ -26,6 +26,7 @@ /* * Copyright 2018 Nexenta Systems, Inc. + * Copyright 2020 RackTop Systems, Inc. */ #include <sys/systm.h> @@ -126,6 +127,24 @@ static int rfs4_fattr4_time_modify_set(); /* * Initialize the supported attributes */ +bitmap4 supported_attrs[3]; + +static void +init_supported_attrs() +{ + bitmap4 supported = rfs4_supported_attrs; + + supported_attrs[0] = supported_attrs[1] = supported_attrs[2] = + rfs4_supported_attrs; + + /* restrict to nfsv4.0 */ + supported_attrs[0] &= ~(FATTR4_SUPPATTR_EXCLCREAT_MASK_LOCAL | + FATTR4_SEC_LABEL_MASK_LOCAL); + + /* restrict to nfsv4.1 */ + supported_attrs[1] &= ~FATTR4_SEC_LABEL_MASK_LOCAL; +} + void rfs4_attr_init() { @@ -143,6 +162,7 @@ rfs4_attr_init() cs.vp = rootvp; cs.fh.nfs_fh4_val = NULL; cs.cr = kcred; + cs.minorversion = 2; /* * Get all the supported attributes @@ -169,6 +189,8 @@ rfs4_attr_init() rfs4_supported_attrs |= nfs4_ntov_map[i].fbit; } } + + init_supported_attrs(); } /* @@ -219,6 +241,7 @@ rfs4_fattr4_supported_attrs(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { int error = 0; + bitmap4 supported = supported_attrs[sarg->cs->minorversion]; switch (cmd) { case NFS4ATTR_SUPPORTED: @@ -226,7 +249,7 @@ rfs4_fattr4_supported_attrs(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: - na->supported_attrs = rfs4_supported_attrs; + na->supported_attrs = supported; break; case NFS4ATTR_SETIT: /* @@ -238,7 +261,7 @@ rfs4_fattr4_supported_attrs(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, /* * Compare the input bitmap to the server's bitmap */ - if (na->supported_attrs != rfs4_supported_attrs) { + if (na->supported_attrs != supported) { error = -1; /* no match */ } break; @@ -2814,6 +2837,40 @@ rfs4_fattr4_time_modify_set(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, return (error); } +/* ARGSUSED */ +static int +rfs4_fattr4_suppattr_exclcreat(nfs4_attr_cmd_t cmd, + struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) +{ + int error = 0; + + /* Not supported for nfs4.0 */ + if (sarg->cs->minorversion == 0) + return (ENOTSUP); + + switch (cmd) { + case NFS4ATTR_SUPPORTED: + if (sarg->op == NFS4ATTR_SETIT) + error = EINVAL; + break; /* this attr is supported */ + case NFS4ATTR_GETIT: + na->supp_exclcreat = RFS4_SUPPATTR_EXCLCREAT; + break; + case NFS4ATTR_SETIT: + /* + * read-only attr + */ + error = EINVAL; + break; + case NFS4ATTR_VERIT: + if (na->supp_exclcreat != RFS4_SUPPATTR_EXCLCREAT) + error = -1; /* no match */ + break; + case NFS4ATTR_FREEIT: + break; + } + return (error); +} static void rfs4_ntov_init(void) @@ -2875,4 +2932,5 @@ rfs4_ntov_init(void) nfs4_ntov_map[53].sv_getit = rfs4_fattr4_time_modify; nfs4_ntov_map[54].sv_getit = rfs4_fattr4_time_modify_set; nfs4_ntov_map[55].sv_getit = rfs4_fattr4_mounted_on_fileid; + nfs4_ntov_map[56].sv_getit = rfs4_fattr4_suppattr_exclcreat; } diff --git a/usr/src/uts/common/fs/nfs/nfs4_state.c b/usr/src/uts/common/fs/nfs/nfs4_state.c index b95dd6fb02..582813bf93 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_state.c +++ b/usr/src/uts/common/fs/nfs/nfs4_state.c @@ -26,6 +26,7 @@ /* * Copyright 2018 Nexenta Systems, Inc. * Copyright 2019 Nexenta by DDN, Inc. + * Copyright 2020 RackTop Systems, Inc. */ #include <sys/systm.h> @@ -595,6 +596,31 @@ rfs4_ss_getstate(vnode_t *dvp, rfs4_ss_pn_t *ss_pn) #define nextdp(dp) ((struct dirent64 *)((char *)(dp) + (dp)->d_reclen)) /* + * Check whether list @head already contains the @client + * This protects against counting the same client twice. + */ +static bool_t +rfs4_ss_has_client(rfs4_oldstate_t *head, nfs_client_id4 *client) +{ + rfs4_oldstate_t *p; + + for (p = head->next; p != head; p = p->next) { + nfs_client_id4 *m = &p->cl_id4; + + if (m->id_len != client->id_len) + continue; + + if (bcmp(m->id_val, client->id_val, client->id_len) == 0) + continue; + + /* client ids match */ + return (TRUE); + } + + return (FALSE); +} + +/* * Add entries from statedir to supplied oldstate list. * Optionally, move all entries from statedir -> destdir. */ @@ -610,6 +636,7 @@ rfs4_ss_oldstate(rfs4_oldstate_t *oldstate, char *statedir, char *destdir) struct uio uio; struct dirent64 *dep; offset_t dirchunk_offset = 0; + unsigned int nclients = 0; /* * open the state directory @@ -670,6 +697,11 @@ rfs4_ss_oldstate(rfs4_oldstate_t *oldstate, char *statedir, char *destdir) } else { cl_ss->ss_pn = ss_pn; } + + if (!rfs4_ss_has_client(oldstate, + &cl_ss->cl_id4)) + nclients++; + insque(cl_ss, oldstate); } else { rfs4_ss_pnfree(ss_pn); @@ -682,6 +714,12 @@ out: VN_RELE(dvp); if (dirt) kmem_free((caddr_t)dirt, RFS4_SS_DIRSIZE); + + if (nclients > 0) { + nfs4_srv_t *nsrv4 = nfs4_get_srv(); + + atomic_add_32(&(nsrv4->nfs4_cur_servinst->nreclaim), nclients); + } } static void @@ -1181,6 +1219,9 @@ rfs4_state_g_init() /* CSTYLED */ rfs4_delegstID_mem_cache = nfs4_init_mem_cache("DelegStateID_entry_cache", 2, sizeof (rfs4_deleg_state_t), 7); + /* CSTYLED */ + (void) nfs4_init_mem_cache("Session_entry_cache", 1, sizeof (rfs4_session_t), 8); + rfs4_client_clrst = rfs4_clear_client_state; } @@ -1480,6 +1521,8 @@ rfs4_state_zone_init(nfs4_srv_t *nsrv4) deleg_state_compare, deleg_state_mkkey, FALSE); + rfs4x_state_init_locked(nsrv4); + mutex_exit(&nsrv4->state_lock); /* @@ -1516,6 +1559,8 @@ rfs4_state_zone_fini() return; } + rfs4x_state_fini(nsrv4); + /* destroy server instances and current instance ptr */ rfs4_servinst_destroy_all(nsrv4); @@ -1720,6 +1765,8 @@ rfs4_client_destroy(rfs4_entry_t u_entry) cv_destroy(cp->rc_cbinfo.cb_cv_nullcaller); list_destroy(&cp->rc_openownerlist); + list_destroy(&cp->rc_sessions); + /* free callback info */ rfs4_cbinfo_free(&cp->rc_cbinfo); @@ -1738,6 +1785,8 @@ rfs4_client_destroy(rfs4_entry_t u_entry) if (cp->rc_sysidt != LM_NOSYSID) lm_free_sysidt(cp->rc_sysidt); + + rfs4_free_cred_set(&cp->rc_cr_set); } static bool_t @@ -1786,6 +1835,7 @@ rfs4_client_create(rfs4_entry_t u_entry, void *arg) /* We need the client to ack us */ cp->rc_need_confirm = TRUE; cp->rc_cp_confirmed = NULL; + cp->rc_destroying = FALSE; /* TRUE all the time until the callback path actually fails */ cp->rc_cbinfo.cb_notified_of_cb_path_down = TRUE; @@ -1793,13 +1843,16 @@ rfs4_client_create(rfs4_entry_t u_entry, void *arg) /* Initialize the access time to now */ cp->rc_last_access = gethrestime_sec(); - cp->rc_cr_set = NULL; + bzero(&cp->rc_cr_set, sizeof (cred_set_t)); cp->rc_sysidt = LM_NOSYSID; list_create(&cp->rc_openownerlist, sizeof (rfs4_openowner_t), offsetof(rfs4_openowner_t, ro_node)); + list_create(&cp->rc_sessions, sizeof (rfs4_session_t), + offsetof(rfs4_session_t, sn_node)); + /* set up the callback control structure */ cp->rc_cbinfo.cb_state = CB_UNINIT; mutex_init(cp->rc_cbinfo.cb_lock, NULL, MUTEX_DEFAULT, NULL); @@ -1815,6 +1868,12 @@ rfs4_client_create(rfs4_entry_t u_entry, void *arg) rfs4_servinst_assign(nsrv4, cp, nsrv4->nfs4_cur_servinst); rfs4_dbe_rele(cp->rc_dbe); + /* + * NFSv4.1: See rfc5661, Section 18.36.4, eir_sequenceid + */ + cp->rc_contrived.xi_sid = 1; + cp->rc_contrived.cs_status = NFS4ERR_SEQ_MISORDERED; + return (TRUE); } @@ -2202,7 +2261,6 @@ rfs4_openowner_create(rfs4_entry_t u_entry, void *arg) oo->ro_open_seqid = seqid; bzero(&oo->ro_reply, sizeof (nfs_resop4)); oo->ro_client = cp; - oo->ro_cr_set = NULL; list_create(&oo->ro_statelist, sizeof (rfs4_state_t), offsetof(rfs4_state_t, rs_node)); @@ -3337,6 +3395,7 @@ rfs4_client_close(rfs4_client_t *cp) rfs4_dbe_unlock(cp->rc_dbe); rfs4_client_state_remove(cp); + rfs4x_client_session_remove(cp); /* Release the client */ rfs4_client_rele(cp); @@ -3461,19 +3520,26 @@ rfs4_get_state(stateid4 *stateid, rfs4_state_t **spp, } int -rfs4_check_stateid_seqid(rfs4_state_t *sp, stateid4 *stateid) +rfs4_check_stateid_seqid(rfs4_state_t *sp, stateid4 *stateid, + const compound_state_t *cs) { stateid_t *id = (stateid_t *)stateid; + bool_t has_session = rfs4_has_session(cs); if (rfs4_lease_expired(sp->rs_owner->ro_client)) return (NFS4_CHECK_STATEID_EXPIRED); + if (has_session && id->bits.chgseq == 0) + return (NFS4_CHECK_STATEID_OKAY); + /* Stateid is some time in the future - that's bad */ if (sp->rs_stateid.bits.chgseq < id->bits.chgseq) return (NFS4_CHECK_STATEID_BAD); - if (sp->rs_stateid.bits.chgseq == id->bits.chgseq + 1) + if (!has_session && + sp->rs_stateid.bits.chgseq == id->bits.chgseq + 1) { return (NFS4_CHECK_STATEID_REPLAY); + } /* Stateid is some time in the past - that's old */ if (sp->rs_stateid.bits.chgseq > id->bits.chgseq) @@ -3490,19 +3556,26 @@ rfs4_check_stateid_seqid(rfs4_state_t *sp, stateid4 *stateid) } int -rfs4_check_lo_stateid_seqid(rfs4_lo_state_t *lsp, stateid4 *stateid) +rfs4_check_lo_stateid_seqid(rfs4_lo_state_t *lsp, stateid4 *stateid, + const compound_state_t *cs) { stateid_t *id = (stateid_t *)stateid; + bool_t has_session = rfs4_has_session(cs); if (rfs4_lease_expired(lsp->rls_state->rs_owner->ro_client)) return (NFS4_CHECK_STATEID_EXPIRED); + if (has_session && id->bits.chgseq == 0) + return (NFS4_CHECK_STATEID_OKAY); + /* Stateid is some time in the future - that's bad */ if (lsp->rls_lockid.bits.chgseq < id->bits.chgseq) return (NFS4_CHECK_STATEID_BAD); - if (lsp->rls_lockid.bits.chgseq == id->bits.chgseq + 1) + if (!has_session && + lsp->rls_lockid.bits.chgseq == id->bits.chgseq + 1) { return (NFS4_CHECK_STATEID_REPLAY); + } /* Stateid is some time in the past - that's old */ if (lsp->rls_lockid.bits.chgseq > id->bits.chgseq) @@ -3663,6 +3736,24 @@ out: return (stat); } +static nfsstat4 +check_state_seqid(stateid_t *st, stateid_t *in, bool_t has_session) +{ + /* rfc56661, section 8.2.2, "seqid to zero" */ + if (has_session && in->bits.chgseq == 0) + return (NFS4_OK); + + /* Seqid in the future? - that's bad */ + if (st->bits.chgseq < in->bits.chgseq) + return (NFS4ERR_BAD_STATEID); + + /* Seqid in the past? - that's old */ + if (st->bits.chgseq > in->bits.chgseq) + return (NFS4ERR_OLD_STATEID); + + return (NFS4_OK); +} + /* * Given the I/O mode (FREAD or FWRITE), the vnode, the stateid and whether * the file is being truncated, return NFS4_OK if allowed or appropriate @@ -3681,7 +3772,7 @@ out: nfsstat4 rfs4_check_stateid(int mode, vnode_t *vp, stateid4 *stateid, bool_t trunc, bool_t *deleg, - bool_t do_access, caller_context_t *ct) + bool_t do_access, caller_context_t *ct, compound_state_t *cs) { rfs4_file_t *fp; bool_t create = FALSE; @@ -3690,6 +3781,7 @@ rfs4_check_stateid(int mode, vnode_t *vp, rfs4_lo_state_t *lsp; stateid_t *id = (stateid_t *)stateid; nfsstat4 stat = NFS4_OK; + bool_t use_ss = rfs4_has_session(cs); if (ct != NULL) { ct->cc_sysid = 0; @@ -3702,6 +3794,7 @@ rfs4_check_stateid(int mode, vnode_t *vp, fp = rfs4_findfile(vp, NULL, &create); if (fp == NULL) return (NFS4_OK); + if (fp->rf_dinfo.rd_dtype == OPEN_DELEGATE_NONE) { rfs4_file_rele(fp); return (NFS4_OK); @@ -3718,6 +3811,7 @@ rfs4_check_stateid(int mode, vnode_t *vp, stat = rfs4_get_all_state(stateid, &sp, &dsp, &lsp); if (stat != NFS4_OK) return (stat); + if (lsp != NULL) { /* Is associated server instance in its grace period? */ if (rfs4_clnt_in_grace(lsp->rls_locker->rl_client)) { @@ -3726,31 +3820,24 @@ rfs4_check_stateid(int mode, vnode_t *vp, rfs4_state_rele_nounlock(sp); return (NFS4ERR_GRACE); } - if (id->bits.type == LOCKID) { - /* Seqid in the future? - that's bad */ - if (lsp->rls_lockid.bits.chgseq < - id->bits.chgseq) { - rfs4_lo_state_rele(lsp, FALSE); - if (sp != NULL) - rfs4_state_rele_nounlock(sp); - return (NFS4ERR_BAD_STATEID); - } - /* Seqid in the past? - that's old */ - if (lsp->rls_lockid.bits.chgseq > - id->bits.chgseq) { - rfs4_lo_state_rele(lsp, FALSE); - if (sp != NULL) - rfs4_state_rele_nounlock(sp); - return (NFS4ERR_OLD_STATEID); - } - /* Ensure specified filehandle matches */ - if (lsp->rls_state->rs_finfo->rf_vp != vp) { - rfs4_lo_state_rele(lsp, FALSE); - if (sp != NULL) - rfs4_state_rele_nounlock(sp); - return (NFS4ERR_BAD_STATEID); - } + + ASSERT(id->bits.type == LOCKID); + stat = check_state_seqid(&lsp->rls_lockid, id, use_ss); + if (stat) { + rfs4_lo_state_rele(lsp, FALSE); + if (sp) + rfs4_state_rele_nounlock(sp); + return (stat); + } + + /* Ensure specified filehandle matches */ + if (lsp->rls_state->rs_finfo->rf_vp != vp) { + rfs4_lo_state_rele(lsp, FALSE); + if (sp != NULL) + rfs4_state_rele_nounlock(sp); + return (NFS4ERR_BAD_STATEID); } + if (ct != NULL) { ct->cc_sysid = lsp->rls_locker->rl_client->rc_sysidt; @@ -3766,18 +3853,13 @@ rfs4_check_stateid(int mode, vnode_t *vp, rfs4_state_rele_nounlock(sp); return (NFS4ERR_GRACE); } + /* Skip if is here via the LOCKID */ if (id->bits.type == OPENID) { - /* Seqid in the future? - that's bad */ - if (sp->rs_stateid.bits.chgseq < - id->bits.chgseq) { + stat = check_state_seqid(&sp->rs_stateid, id, + use_ss); + if (stat) { rfs4_state_rele_nounlock(sp); - return (NFS4ERR_BAD_STATEID); - } - /* Seqid in the past - that's old */ - if (sp->rs_stateid.bits.chgseq > - id->bits.chgseq) { - rfs4_state_rele_nounlock(sp); - return (NFS4ERR_OLD_STATEID); + return (stat); } } /* Ensure specified filehandle matches */ @@ -3842,9 +3924,11 @@ rfs4_check_stateid(int mode, vnode_t *vp, rfs4_deleg_state_rele(dsp); return (NFS4ERR_GRACE); } - if (dsp->rds_delegid.bits.chgseq != id->bits.chgseq) { + + stat = check_state_seqid(&dsp->rds_delegid, id, use_ss); + if (stat) { rfs4_deleg_state_rele(dsp); - return (NFS4ERR_BAD_STATEID); + return (stat); } /* Ensure specified filehandle matches */ diff --git a/usr/src/uts/common/fs/nfs/nfs4_xdr.c b/usr/src/uts/common/fs/nfs/nfs4_xdr.c index 36b19fe657..1879482a14 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_xdr.c +++ b/usr/src/uts/common/fs/nfs/nfs4_xdr.c @@ -21,6 +21,7 @@ /* * Copyright 2016 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 RackTop Systems, Inc. */ /* @@ -65,6 +66,7 @@ bool_t xdr_bitmap4(XDR *xdrs, bitmap4 *objp) { int32_t len, size; + uint32_t bmval2 = 0; if (xdrs->x_op == XDR_FREE) return (TRUE); @@ -75,7 +77,15 @@ xdr_bitmap4(XDR *xdrs, bitmap4 *objp) * uint64_t and ignore all of the rest. */ if (xdrs->x_op == XDR_ENCODE) { - len = 2; + bitmap4 obj = *objp; + + objp = &obj; + if (*objp & FATTR4_SUPPATTR_EXCLCREAT_MASK_LOCAL) { + bmval2 |= 1ULL << (FATTR4_SUPPATTR_EXCLCREAT - 64); + *objp &= ~FATTR4_SUPPATTR_EXCLCREAT_MASK_LOCAL; + } + + len = bmval2 ? 3 : 2; if (!XDR_PUTINT32(xdrs, &len)) return (FALSE); @@ -83,15 +93,19 @@ xdr_bitmap4(XDR *xdrs, bitmap4 *objp) #if defined(_LITTLE_ENDIAN) if (XDR_PUTINT32(xdrs, (int32_t *)((char *)objp + BYTES_PER_XDR_UNIT)) == TRUE) { - return (XDR_PUTINT32(xdrs, (int32_t *)objp)); + if (!XDR_PUTINT32(xdrs, (int32_t *)objp)) + return (FALSE); } #elif defined(_BIG_ENDIAN) if (XDR_PUTINT32(xdrs, (int32_t *)objp) == TRUE) { - return (XDR_PUTINT32(xdrs, (int32_t *)((char *)objp + - BYTES_PER_XDR_UNIT))); + if (!XDR_PUTINT32(xdrs, (int32_t *)((char *)objp + + BYTES_PER_XDR_UNIT))) + return (FALSE); } #endif - return (FALSE); + if (len == 3 && !XDR_PUTINT32(xdrs, (int32_t *)&bmval2)) + return (FALSE); + return (TRUE); } if (!XDR_GETINT32(xdrs, &len)) @@ -100,19 +114,28 @@ xdr_bitmap4(XDR *xdrs, bitmap4 *objp) /* * Common fast DECODE cases */ - if (len == 2) { + if (len == 2 || len == 3) { #if defined(_LITTLE_ENDIAN) if (XDR_GETINT32(xdrs, (int32_t *)((char *)objp + BYTES_PER_XDR_UNIT)) == TRUE) { - return (XDR_GETINT32(xdrs, (int32_t *)objp)); + if (!XDR_GETINT32(xdrs, (int32_t *)objp)) + return (FALSE); } #elif defined(_BIG_ENDIAN) if (XDR_GETINT32(xdrs, (int32_t *)objp) == TRUE) { - return (XDR_GETINT32(xdrs, (int32_t *)((char *)objp + - BYTES_PER_XDR_UNIT))); + if (!XDR_GETINT32(xdrs, (int32_t *)((char *)objp + + BYTES_PER_XDR_UNIT))) + return (FALSE); } #endif - return (FALSE); + if (len == 3) { + if (!XDR_GETINT32(xdrs, (int32_t *)&bmval2)) + return (FALSE); + if (bmval2 & (1ULL << (FATTR4_SUPPATTR_EXCLCREAT - 64))) + *objp |= FATTR4_SUPPATTR_EXCLCREAT_MASK_LOCAL; + } + + return (TRUE); } *objp = 0; @@ -134,6 +157,12 @@ xdr_bitmap4(XDR *xdrs, bitmap4 *objp) return (FALSE); if (--len == 0) return (TRUE); + + if (!XDR_GETINT32(xdrs, (int32_t *)&bmval2)) + return (FALSE); + if (bmval2 & (1ULL << (FATTR4_SUPPATTR_EXCLCREAT - 64))) + *objp |= FATTR4_SUPPATTR_EXCLCREAT_MASK_LOCAL; + if (!XDR_GETINT32(xdrs, (int32_t *)((char *)objp + BYTES_PER_XDR_UNIT))) return (FALSE); #else @@ -661,7 +690,7 @@ xdr_fs_location4(XDR *xdrs, fs_location4 *objp) } /* Called by xdr_array */ -static bool_t +bool_t xdr_nfsace4(XDR *xdrs, nfsace4 *objp) { if (xdrs->x_op != XDR_FREE) { @@ -755,6 +784,33 @@ xdr_nfstime4(XDR *xdrs, nfstime4 *objp) return (xdr_u_int(xdrs, &objp->nseconds)); } +bool_t +xdr_fattr4_sec_label(XDR *xdrs, fattr4_sec_label *objp) +{ + uint_t dummy = 0; + + if (xdrs->x_op == XDR_FREE) { + /* + * Optimized free case + */ + if (objp->slai_val != NULL) + kmem_free(objp->slai_val, objp->slai_len); + return (TRUE); + } + + /* + * For now use a 0 here to indicate the null translation; in + * the future we place a call to translation code here. + */ + if (!xdr_u_int(xdrs, &dummy)) /* lfs */ + return (FALSE); + + if (!xdr_u_int(xdrs, &dummy)) /* pi */ + return (FALSE); + + return (xdr_bytes(xdrs, (char **)&objp->slai_val, + (uint_t *)&objp->slai_len, NFS4_OPAQUE_LIMIT)); +} /* * structured used for calls into xdr_ga_fattr_res() as a means @@ -2510,7 +2566,7 @@ xdr_settime4(XDR *xdrs, settime4 *objp) return (xdr_u_int(xdrs, &objp->time.nseconds)); } -static bool_t +bool_t xdr_fattr4(XDR *xdrs, fattr4 *objp) { if (xdrs->x_op != XDR_FREE) { @@ -2882,12 +2938,29 @@ xdr_LOCKU4args(XDR *xdrs, LOCKU4args *objp) } static bool_t +xdr_share_access(XDR *xdrs, uint32_t *share_access, uint32_t *deleg_want) +{ + uint32_t w; + + if (xdrs->x_op == XDR_DECODE) { + if (!xdr_u_int(xdrs, &w)) + return (FALSE); + + *share_access = w & OPEN4_SHARE_ACCESS_MASK; + *deleg_want = w & OPEN4_SHARE_WANT_MASK; + } + + return (TRUE); +} + +static bool_t xdr_OPEN4args(XDR *xdrs, OPEN4args *objp) { if (xdrs->x_op != XDR_FREE) { if (!xdr_u_int(xdrs, &objp->seqid)) return (FALSE); - if (!xdr_u_int(xdrs, &objp->share_access)) + if (!xdr_share_access(xdrs, &objp->share_access, + &objp->deleg_want)) return (FALSE); if (!xdr_u_int(xdrs, &objp->share_deny)) return (FALSE); @@ -2922,47 +2995,21 @@ xdr_OPEN4args(XDR *xdrs, OPEN4args *objp) createverf)) return (FALSE); break; + case EXCLUSIVE4_1: + if (!xdr_creatverfattr(xdrs, + &objp->createhow4_u.ch_createboth)) + return (FALSE); + break; default: return (FALSE); } } /* xdr_open_claim4 */ - if (!xdr_int(xdrs, (int *)&objp->claim)) - return (FALSE); - - switch (objp->claim) { - case CLAIM_NULL: - return (xdr_bytes(xdrs, (char **)&objp->open_claim4_u. - file.utf8string_val, - (uint_t *)&objp->open_claim4_u.file. - utf8string_len, - NFS4_MAX_UTF8STRING)); - case CLAIM_PREVIOUS: - return (xdr_int(xdrs, - (int *)&objp->open_claim4_u.delegate_type)); - case CLAIM_DELEGATE_CUR: - if (!xdr_u_int(xdrs, (uint_t *)&objp->open_claim4_u. - delegate_cur_info.delegate_stateid.seqid)) - return (FALSE); - if (!xdr_opaque(xdrs, objp->open_claim4_u. - delegate_cur_info.delegate_stateid.other, - NFS4_OTHER_SIZE)) - return (FALSE); - return (xdr_bytes(xdrs, (char **)&objp->open_claim4_u. - delegate_cur_info.file.utf8string_val, - (uint_t *)&objp->open_claim4_u. - delegate_cur_info.file.utf8string_len, - NFS4_MAX_UTF8STRING)); - case CLAIM_DELEGATE_PREV: - return (xdr_bytes(xdrs, (char **)&objp->open_claim4_u. - file_delegate_prev.utf8string_val, - (uint_t *)&objp->open_claim4_u. - file_delegate_prev.utf8string_len, - NFS4_MAX_UTF8STRING)); - default: + if (!xdr_open_claim4(xdrs, &objp->claim)) return (FALSE); - } + + return (TRUE); } /* @@ -2984,35 +3031,8 @@ xdr_OPEN4args(XDR *xdrs, OPEN4args *objp) } } - switch (objp->claim) { - case CLAIM_NULL: - if (objp->open_claim4_u.file.utf8string_val != NULL) - kmem_free(objp->open_claim4_u.file.utf8string_val, - objp->open_claim4_u.file.utf8string_len); - return (TRUE); - case CLAIM_PREVIOUS: - return (TRUE); - case CLAIM_DELEGATE_CUR: - if (objp->open_claim4_u.delegate_cur_info.file.utf8string_val != - NULL) { - kmem_free(objp->open_claim4_u.delegate_cur_info.file. - utf8string_val, - objp->open_claim4_u.delegate_cur_info.file. - utf8string_len); - } - return (TRUE); - case CLAIM_DELEGATE_PREV: - if (objp->open_claim4_u.file_delegate_prev.utf8string_val != - NULL) { - kmem_free(objp->open_claim4_u.file_delegate_prev. - utf8string_val, - objp->open_claim4_u.file_delegate_prev. - utf8string_len); - } - return (TRUE); - default: - return (TRUE); - } + (void) xdr_open_claim4(xdrs, &objp->claim); + return (TRUE); } static bool_t @@ -3284,7 +3304,7 @@ xdr_OPEN_DOWNGRADE4args(XDR *xdrs, OPEN_DOWNGRADE4args *objp) return (FALSE); if (!xdr_u_int(xdrs, &objp->seqid)) return (FALSE); - if (!xdr_u_int(xdrs, &objp->share_access)) + if (!xdr_share_access(xdrs, &objp->share_access, &objp->deleg_want)) return (FALSE); return (xdr_u_int(xdrs, &objp->share_deny)); } @@ -3792,6 +3812,18 @@ xdr_secinfo4(XDR *xdrs, secinfo4 *objp) return (TRUE); } +bool_t +xdr_SECINFO4res(XDR *xdrs, SECINFO4res *objp) +{ + if (!xdr_int(xdrs, (int32_t *)&objp->status)) + return (FALSE); + if (objp->status != NFS4_OK) + return (TRUE); + return (xdr_array(xdrs, (char **)&objp->SECINFO4resok_val, + (uint_t *)&objp->SECINFO4resok_len, ~0, sizeof (secinfo4), + (xdrproc_t)xdr_secinfo4)); +} + static bool_t xdr_SETCLIENTID4args(XDR *xdrs, SETCLIENTID4args *objp) { @@ -3948,6 +3980,13 @@ xdr_snfs_argop4_free(XDR *xdrs, nfs_argop4 **arrayp, int len) return (TRUE); for (i = 0; i < len; i++) { + /* Freeing for nfs4.x */ + if (array[i].argop >= OP_BACKCHANNEL_CTL && + array[i].argop != OP_ILLEGAL) { + xdr_nfs4x_argop4(xdrs, &array[i]); + continue; + } + /* * These should be ordered by frequency of use */ @@ -4309,6 +4348,10 @@ xdr_snfs_argop4(XDR *xdrs, nfs_argop4 *objp) return (xdr_decode_nfs_fh4(xdrs, &objp->nfs_argop4_u.opputfh.object)); default: + if (objp->argop >= OP_BACKCHANNEL_CTL && + objp->argop != OP_ILLEGAL) + return (xdr_nfs4x_argop4(xdrs, objp)); + return (xdr_nfs_argop4(xdrs, objp)); } } @@ -4775,6 +4818,10 @@ xdr_snfs_resop4(XDR *xdrs, nfs_resop4 *objp) return (xdr_encode_nfs_fh4(xdrs, &objp->nfs_resop4_u.opgetfh.object)); default: + if (objp->resop >= OP_BACKCHANNEL_CTL && + objp->resop != OP_ILLEGAL) + return (xdr_nfs4x_resop4(xdrs, objp)); + return (xdr_nfs_resop4(xdrs, objp)); } } @@ -5176,32 +5223,6 @@ xdr_cnfs_cb_argop4(XDR *xdrs, nfs_cb_argop4 *objp) return (FALSE); } -static bool_t -xdr_nfs_cb_resop4(XDR *xdrs, nfs_cb_resop4 *objp) -{ - if (!xdr_u_int(xdrs, &objp->resop)) - return (FALSE); - switch (objp->resop) { - case OP_CB_GETATTR: - if (!xdr_int(xdrs, - (int32_t *)&objp->nfs_cb_resop4_u.opcbgetattr. - status)) - return (FALSE); - if (objp->nfs_cb_resop4_u.opcbgetattr.status != NFS4_OK) - return (TRUE); - return (xdr_fattr4(xdrs, - &objp->nfs_cb_resop4_u.opcbgetattr. - obj_attributes)); - case OP_CB_RECALL: - return (xdr_int(xdrs, - (int32_t *)&objp->nfs_cb_resop4_u.opcbrecall.status)); - case OP_CB_ILLEGAL: - return (xdr_int(xdrs, - (int32_t *)&objp->nfs_cb_resop4_u.opcbillegal.status)); - } - return (FALSE); -} - /* * The NFS client side callback, RPC server */ diff --git a/usr/src/uts/common/fs/nfs/nfs4x_dispatch.c b/usr/src/uts/common/fs/nfs/nfs4x_dispatch.c new file mode 100644 index 0000000000..49ae2d6ad4 --- /dev/null +++ b/usr/src/uts/common/fs/nfs/nfs4x_dispatch.c @@ -0,0 +1,177 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2017 RackTop Systems. + */ + +#include <sys/systm.h> +#include <sys/sdt.h> +#include <rpc/types.h> +#include <rpc/auth.h> +#include <rpc/auth_unix.h> +#include <rpc/auth_des.h> +#include <rpc/svc.h> +#include <rpc/xdr.h> +#include <nfs/nfs4.h> +#include <nfs/nfs_dispatch.h> +#include <sys/cmn_err.h> +#include <sys/modctl.h> + +static void +rfs4_err_resp(COMPOUND4args *args, COMPOUND4res *resp, nfsstat4 err) +{ + size_t sz; + + resp->array_len = 1; + sz = resp->array_len * sizeof (nfs_resop4); + resp->array = kmem_zalloc(sz, KM_SLEEP); + + resp->array[0].resop = args->array[0].argop; + resp->status = resp->array[0].nfs_resop4_u.opillegal.status = err; +} + +/* + * The function checks if given compound operation is allowed + * to be the very fist operation in compound array. + */ +static bool_t +valid_first_compound_op(nfs_opnum4 op) +{ + if (op == OP_BIND_CONN_TO_SESSION || + op == OP_SEQUENCE || + op == OP_EXCHANGE_ID || + op == OP_CREATE_SESSION || + op == OP_DESTROY_SESSION || + op == OP_DESTROY_CLIENTID || + op == OP_ILLEGAL) + return (TRUE); + + return (FALSE); +} + +/* + * The function verifies arguments passed to mds_op_compound. + * If agrguments are valid, NFS4_OK is returned, otherwise + * function returns correspoinding NFS4 error code. + */ +static nfsstat4 +verify_compound_args(COMPOUND4args *args) +{ + if (args->array_len == 0) + return (NFS4_OK); + + if (!valid_first_compound_op(args->array[0].argop)) + return (NFS4ERR_OP_NOT_IN_SESSION); + + if (args->array_len > 1 && args->array[0].argop != OP_SEQUENCE) { + /* + * Compound is outside the session. There must be + * only one operation in request. + */ + return (NFS4ERR_NOT_ONLY_OP); + } + + return (NFS4_OK); +} + +static void +rfs4x_dispatch_done(compound_state_t *cs) +{ + if (cs->slot) + rfs4x_sequence_done(cs->cmpresp, cs); + else { + rfs4_compound_free(cs->cmpresp); + } + cs->cs_flags |= RFS4_DISPATCH_DONE; +} + +static bool_t +xdr_compound_wrapper(XDR *xdrs, compound_state_t *cs) +{ + COMPOUND4res *resp = cs->cmpresp; + bool_t res = FALSE; + bool_t isreal = (xdrs->x_handy != 0); /* real data encoding ? */ + + if (!(cs->cs_flags & RFS4_DISPATCH_DONE)) { + res = xdr_COMPOUND4res_srv(xdrs, resp); + if (isreal) + rfs4x_dispatch_done(cs); + } + + return (res); +} + +int +rfs4x_dispatch(struct svc_req *req, SVCXPRT *xprt, char *ap) +{ + struct compound_state cs; + COMPOUND4res res_buf; + COMPOUND4res *rbp; + COMPOUND4args *cap; + int rpcerr = 0; + nfsstat4 error; + + bzero(&res_buf, sizeof (COMPOUND4res)); + rbp = &res_buf; + cap = (COMPOUND4args *)ap; + rfs4_init_compound_state(&cs); + + cs.statusp = &error; + cs.cmpresp = rbp; + + error = verify_compound_args(cap); + if (error != NFS4_OK) { /* NFS4_OK is 0 */ + rfs4_err_resp(cap, rbp, error); + goto out_send; + } + + error = rfs4x_sequence_prep(cap, rbp, &cs); + if (error != NFS4_OK) { + if (error != nfserr_replay_cache) + rfs4_err_resp(cap, rbp, error); + goto out_send; + } + + /* Regular processing */ + curthread->t_flag |= T_DONTPEND; + rfs4_compound(cap, rbp, &cs, req, &rpcerr); + curthread->t_flag &= ~T_DONTPEND; + + /* + * On RPC error, short sendreply + */ + if (rpcerr) { + goto out_free; + } + + if (curthread->t_flag & T_WOULDBLOCK) { + curthread->t_flag &= ~T_WOULDBLOCK; + error = 1; + goto out_free; + } + +out_send: + if (!svc_sendreply(xprt, xdr_compound_wrapper, (char *)&cs)) { + DTRACE_PROBE2(sendfail, SVCXPRT *, xprt, + compound_state_t *, &cs); + svcerr_systemerr(xprt); + rpcerr = 1; + } + +out_free: + if (!(cs.cs_flags & RFS4_DISPATCH_DONE)) { + rfs4x_dispatch_done(&cs); + } + + rfs4_fini_compound_state(&cs); + return ((error != NFS4_OK || rpcerr) ? 1 : 0); +} diff --git a/usr/src/uts/common/fs/nfs/nfs4x_srv.c b/usr/src/uts/common/fs/nfs/nfs4x_srv.c new file mode 100644 index 0000000000..854a8e1abe --- /dev/null +++ b/usr/src/uts/common/fs/nfs/nfs4x_srv.c @@ -0,0 +1,1250 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 RackTop Systems. + */ + +#include <rpc/types.h> +#include <rpc/auth.h> +#include <rpc/rpcsec_gss.h> +#include <sys/sdt.h> +#include <sys/disp.h> +#include <nfs/nfs.h> +#include <nfs/nfs4.h> +#include <sys/systeminfo.h> + +/* Helpers */ + +/* Principal handling routines */ +/* returns 0 if no match; or 1 for a match */ +int +rfs4_cmp_cred_set(cred_set_t *p, struct compound_state *cs) +{ + int rc = 0; + rpc_gss_principal_t recp; /* cached clnt princ */ + rpc_gss_principal_t ibrp; /* inbound req princ */ + + + if (p->cp_cr == NULL) + return (rc); /* nothing to compare with */ + + if (p->cp_aflavor != cs->req->rq_cred.oa_flavor) + return (rc); + + if (p->cp_secmod != cs->nfsflavor) + return (rc); + + if (crcmp(p->cp_cr, cs->basecr)) + return (rc); + + switch (p->cp_aflavor) { + case AUTH_DES: + rc = (strcmp(p->cp_princ, cs->principal) == 0); + break; + + case RPCSEC_GSS: + recp = (rpc_gss_principal_t)p->cp_princ; + ibrp = (rpc_gss_principal_t)cs->principal; + + if (recp->len != ibrp->len) + break; + rc = (bcmp(recp->name, ibrp->name, ibrp->len) == 0); + break; + + case AUTH_SYS: + case AUTH_NONE: + default: + rc = 1; + break; + } + return (rc); +} + +static rpc_gss_principal_t +rfs4_dup_princ(rpc_gss_principal_t ppl) +{ + rpc_gss_principal_t pdup; + int len; + + if (ppl == NULL) + return (NULL); + + len = sizeof (int) + ppl->len; + pdup = (rpc_gss_principal_t)kmem_alloc(len, KM_SLEEP); + bcopy(ppl, pdup, len); + return (pdup); +} + +void +rfs4_set_cred_set(cred_set_t *p, struct compound_state *cs) +{ + ASSERT(p->cp_cr == NULL); + + p->cp_cr = crdup(cs->basecr); + p->cp_aflavor = cs->req->rq_cred.oa_flavor; + p->cp_secmod = cs->nfsflavor; /* secmod != flavor for RPCSEC_GSS */ + + /* + * Set principal as per security flavor + */ + switch (p->cp_aflavor) { + case AUTH_DES: + p->cp_princ = strdup(cs->principal); + break; + + case RPCSEC_GSS: + p->cp_princ = + (caddr_t)rfs4_dup_princ((rpc_gss_principal_t)cs->principal); + break; + + case AUTH_SYS: + case AUTH_NONE: + default: + break; + } +} + +void +rfs4_free_cred_set(cred_set_t *p) +{ + rpc_gss_principal_t ppl; + + if (p->cp_cr == NULL) + return; + + switch (p->cp_aflavor) { + case AUTH_DES: + kmem_free(p->cp_princ, strlen(p->cp_princ) + 1); + break; + + case RPCSEC_GSS: + ppl = (rpc_gss_principal_t)p->cp_princ; + kmem_free(ppl, ppl->len + sizeof (int)); + break; + } + + crfree(p->cp_cr); + p->cp_cr = NULL; +} + +/* principal end */ + +bool_t +nfs_clid4_cmp(nfs_client_id4 *s1, nfs_client_id4 *s2) +{ + if (s1->verifier != s2->verifier) + return (FALSE); + if (s1->id_len != s2->id_len) + return (FALSE); + if (bcmp(s1->id_val, s2->id_val, s2->id_len)) + return (FALSE); + return (TRUE); +} + +/* + * Rudimentary server implementation (XXX - for now) + */ +void +rfs4x_get_server_impl_id(EXCHANGE_ID4resok *resp) +{ + char *sol_impl = "illumos NFSv4.1 Server Implementation"; + char *sol_idom = "nfsv41.ietf.org"; + void *p; + uint_t len = 0; + nfs_impl_id4 *nip; + + resp->eir_server_impl_id.eir_server_impl_id_len = 1; + nip = kmem_zalloc(sizeof (nfs_impl_id4), KM_SLEEP); + resp->eir_server_impl_id.eir_server_impl_id_val = nip; + + /* Domain */ + nip->nii_domain.utf8string_len = len = strlen(sol_idom); + p = kmem_zalloc(len * sizeof (char), KM_SLEEP); + nip->nii_domain.utf8string_val = p; + bcopy(sol_idom, p, len); + + /* Implementation */ + nip->nii_name.utf8string_len = len = strlen(sol_impl); + p = kmem_zalloc(len * sizeof (char), KM_SLEEP); + nip->nii_name.utf8string_val = p; + bcopy(sol_impl, p, len); + + /* Time is zero for now */ +} + +/* + * Compute the "use bits", i.e. the flags specifying the permissible + * regular, MDS, and data server ops for the returned clientid. + * + * The minorversion1 specification allows a server implementor two + * alternatives: allow PNFS_MDS and PNFS_DS on the same clientid, or + * force the client to create separate clientids to distinguish + * MDS versus DS operations. + * + * Our design distinguishes operations based upon filehandle, and thus + * there is no reason to force the client to create separate clientids. + * Thus, we give the client as much as possible, while keeping the result + * within the allowed combinations as specified in the specification. + * + * Our constraints are: use a subset of the client's request, unless + * the client requested nothing, in which case we may return any + * legal combination; and, the combination of NON_PNFS and PNFS_MDS + * may not both be set in the results. These constraints are reflected + * in the ASSERT()s at the end. + */ + +static uint32_t +compute_use_pnfs_flags(uint32_t request) +{ + uint32_t rc; + + /* Start with the client's initial request */ + rc = request & EXCHGID4_FLAG_MASK_PNFS; + + /* If the client requested nothing, return the most permissive. */ + if (rc == 0) { + rc = EXCHGID4_FLAG_USE_NON_PNFS; + goto done; + } + + /* Don't permit the illegal combination of MDS and NON_PNFS */ + if ((rc & + (EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_USE_PNFS_MDS)) == + (EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_USE_PNFS_MDS)) + rc &= ~EXCHGID4_FLAG_USE_PNFS_MDS; +done: + ASSERT(((request & EXCHGID4_FLAG_MASK_PNFS) == 0) || + ((rc & ~(request & EXCHGID4_FLAG_MASK_PNFS)) == 0)); + ASSERT((rc & (EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_USE_PNFS_MDS)) + != (EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_USE_PNFS_MDS)); + ASSERT(rc != 0); + + return (rc); +} + +static void +rfs4x_set_trunkinfo(EXCHANGE_ID4resok *rok) +{ + const char *nodename = uts_nodename(); + unsigned int nd_len = strlen(nodename); + unsigned int hw_len = strlen(hw_serial); + unsigned id_len = nd_len + 1 + hw_len; + char *s = kmem_alloc(id_len, KM_SLEEP); + server_owner4 *so = &rok->eir_server_owner; + struct eir_server_scope *ss = &rok->eir_server_scope; + + (void) memcpy(s, nodename, nd_len); + s[nd_len] = ' '; + (void) memcpy(s + nd_len + 1, hw_serial, hw_len); + + so->so_major_id.so_major_id_len = id_len; + so->so_major_id.so_major_id_val = s; + + ss->eir_server_scope_len = id_len; + ss->eir_server_scope_val = kmem_alloc(id_len, KM_SLEEP); + (void) memcpy(ss->eir_server_scope_val, s, id_len); + + rok->eir_server_owner.so_minor_id = 0; +} + +static bool_t +client_has_state_locked(rfs4_client_t *cp) +{ + if (list_head(&cp->rc_sessions) != NULL || + list_head(&cp->rc_openownerlist) != NULL) + return (TRUE); + else + return (FALSE); +} + +/* OPERATIONS */ + +/* EXCHANGE_ID */ +void +rfs4x_op_exchange_id(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, compound_state_t *cs) +{ + EXCHANGE_ID4args *args = &argop->nfs_argop4_u.opexchange_id; + EXCHANGE_ID4res *resp = &resop->nfs_resop4_u.opexchange_id; + EXCHANGE_ID4resok *rok = &resp->EXCHANGE_ID4res_u.eir_resok4; + rfs4_client_t *cp, *conf; + bool_t update, create; + client_owner4 *cop; + nfs_client_id4 cid; /* cip */ + nfsstat4 status = NFS4_OK; + nfs4_srv_t *nsrv4; + + DTRACE_NFSV4_2(op__exchange__id__start, + struct compound_state *, cs, + EXCHANGE_ID4args *, args); + + /* + * EXCHANGE_ID's may be preceded by SEQUENCE + * + * Check that eia_flags only has "valid" spec bits + * and that no 'eir_flag' ONLY bits are specified. + */ + if (args->eia_flags & ~EXID4_FLAG_MASK) { + status = NFS4ERR_INVAL; + goto err; + } + + update = (args->eia_flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A); + cop = &args->eia_clientowner; + conf = NULL; + + cid.verifier = cop->co_verifier; + cid.id_len = cop->co_ownerid.co_ownerid_len; + cid.id_val = cop->co_ownerid.co_ownerid_val; + cid.cl_addr = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf; + + /* + * Refer to Section 18.35.4 + */ +again: + create = TRUE; + cp = rfs4_findclient(&cid, &create, conf); + ASSERT(cp != NULL); + + if (conf) { + rfs4_dbe_lock(cp->rc_dbe); + if (cp->rc_cp_confirmed == NULL) + cp->rc_cp_confirmed = conf; + else + rfs4_client_rele(conf); + rfs4_dbe_unlock(cp->rc_dbe); + conf = NULL; + } + + if (create) { + /* Record just created */ + if (!update) { + /* case 1 - utok */ + rfs4_set_cred_set(&cp->rc_cr_set, cs); + + rok->eir_clientid = cp->rc_clientid; + rok->eir_sequenceid = cp->rc_contrived.xi_sid; + goto out; + } else { + /* no record and trying to update */ + status = NFS4ERR_NOENT; + goto err_out; + } + } + + /* Record exists */ + + /* expired clients should be ignored and released */ + if (rfs4_lease_expired(cp)) { + rfs4_client_close(cp); + update = FALSE; + goto again; + } + + if (cp->rc_need_confirm) { + /* UNCONFIRMED */ + if (!update) { + /* case 4 - utok */ + rfs4_client_close(cp); + + ASSERT(!update); + goto again; + } else { + /* case 7 - utok */ + status = NFS4ERR_NOENT; + goto err_out; + } + } + + /* record exists and confirmed */ + if (!update) { + if (!rfs4_cmp_cred_set(&cp->rc_cr_set, cs)) { + /* case 3 */ + /* lease is checked above */ + rfs4_dbe_lock(cp->rc_dbe); + if (!client_has_state_locked(cp)) { + rfs4_dbe_unlock(cp->rc_dbe); + + rfs4_client_close(cp); + ASSERT(!update); + goto again; + } + rfs4_dbe_unlock(cp->rc_dbe); + + /* + * clid_in_use. old_client_ret has unexpired + * lease with state. + */ + status = NFS4ERR_CLID_INUSE; + goto err_out; + } else if (cp->rc_nfs_client.verifier != cid.verifier) { + /* case 5: Client Restart */ + /* + * Skip confirmed client record to allow confirmed + * and unconfirmed state at the same time. The number + * of states can collapse to one once the server + * receives an applicable CREATE_SESSION or EXCHANGE_ID. + */ + ASSERT(conf == NULL); + conf = cp; + ASSERT(!update); + goto again; + + } else if (nfs_clid4_cmp(&cp->rc_nfs_client, &cid)) { + /* case 2 - utok */ + rok->eir_clientid = cp->rc_clientid; + rok->eir_sequenceid = cp->rc_contrived.xi_sid; + /* trickle down to "out" */ + + } else { + /* something is really wacky in srv state */ + status = NFS4ERR_SERVERFAULT; + goto err_out; + } + + } else { /* UPDATE */ + if (cp->rc_nfs_client.verifier != cid.verifier) { + /* 18.35.4 case 8 */ + status = NFS4ERR_NOT_SAME; + goto err_out; + } + if (!rfs4_cmp_cred_set(&cp->rc_cr_set, cs)) { + /* 18.35.4 case 9 */ + status = NFS4ERR_PERM; + goto err_out; + } + + /* case 6 - utok */ + rok->eir_clientid = cp->rc_clientid; + rok->eir_sequenceid = cp->rc_contrived.xi_sid; + /* trickle down to "out" */ + } +out: + rok->eir_flags = 0; + if (resp->eir_status == NFS4_OK && !cp->rc_need_confirm) + rok->eir_flags |= EXCHGID4_FLAG_CONFIRMED_R; + + /* + * State Protection Mojo + */ + cp->rc_state_prot.sp_type = args->eia_state_protect.spa_how; + switch (cp->rc_state_prot.sp_type) { + case SP4_NONE: + break; + + case SP4_MACH_CRED: + /* XXX - Some of Karen's secret sauce here... */ + break; + + case SP4_SSV: + /* XXX - ... and here */ +#define ssv_args eia_state_protect.state_protect4_a_u.spa_ssv_parms + if (args->ssv_args.ssp_ops.spo_must_allow & OP_EXCHANGE_ID) { + /* + * if the client ID was created specifying SP4_SSV + * state protection and EXCHANGE_ID as the one of + * the operations in spo_must_allow, then server MUST + * authorize EXCHANGE_IDs with the SSV principal in + * addition to the principal that created the client + * ID. + */ + /* EMPTY */; + } + break; + } + + /* + * Referrals supports + */ + if (args->eia_flags & EXCHGID4_FLAG_SUPP_MOVED_REFER) { + rok->eir_flags |= EXCHGID4_FLAG_SUPP_MOVED_REFER; + } + + /* + * Migration/Replication not (yet) supported + */ + if (args->eia_flags & EXCHGID4_FLAG_SUPP_MOVED_MIGR) + rok->eir_flags &= ~EXCHGID4_FLAG_SUPP_MOVED_MIGR; + + /* + * TODO: Add the appropriate "use_pnfs" flags. + */ + rok->eir_flags |= compute_use_pnfs_flags(args->eia_flags); + + /* force no state protection for now */ + rok->eir_state_protect.spr_how = SP4_NONE; + + /* Implementation specific mojo */ + if (args->eia_client_impl_id.eia_client_impl_id_len != 0) { + /* EMPTY */; + } + + nsrv4 = nfs4_get_srv(); + + /* XXX - jw - best guess */ + rfs4_ss_clid(nsrv4, cp); + + /* Server's implementation */ + rfs4x_get_server_impl_id(rok); + + /* compute trunking capabilities */ + bzero(&rok->eir_server_scope, sizeof (rok->eir_server_scope)); + bzero(&rok->eir_server_owner, sizeof (server_owner4)); + + /* Add trunk handling */ + rfs4x_set_trunkinfo(rok); + + /* + * XXX - jw - best guess + * Check to see if client can perform reclaims + */ + rfs4_ss_chkclid(nsrv4, cp); + +err_out: + rfs4_client_rele(cp); +err: + *cs->statusp = resp->eir_status = status; + + DTRACE_NFSV4_2(op__exchange__id__done, + struct compound_state *, cs, + EXCHANGE_ID4res *, resp); +} + +void +rfs4x_exchange_id_free(nfs_resop4 *resop) +{ + EXCHANGE_ID4res *resp = &resop->nfs_resop4_u.opexchange_id; + EXCHANGE_ID4resok *rok = &resp->EXCHANGE_ID4res_u.eir_resok4; + struct server_owner4 *sop = &rok->eir_server_owner; + nfs_impl_id4 *nip; + int len = 0; + + /* Server Owner: major */ + if ((len = sop->so_major_id.so_major_id_len) != 0) + kmem_free(sop->so_major_id.so_major_id_val, len); + + if ((nip = rok->eir_server_impl_id.eir_server_impl_id_val) != NULL) { + /* Immplementation */ + len = nip->nii_name.utf8string_len; + kmem_free(nip->nii_name.utf8string_val, len * sizeof (char)); + + /* Domain */ + len = nip->nii_domain.utf8string_len; + kmem_free(nip->nii_domain.utf8string_val, len * sizeof (char)); + + /* Server Impl */ + kmem_free(nip, sizeof (nfs_impl_id4)); + } +} + +void +rfs4x_op_create_session(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, compound_state_t *cs) +{ + CREATE_SESSION4args *args = &argop->nfs_argop4_u.opcreate_session; + CREATE_SESSION4res *resp = &resop->nfs_resop4_u.opcreate_session; + CREATE_SESSION4resok *rok = &resp->CREATE_SESSION4res_u.csr_resok4; + CREATE_SESSION4resok *crp; + rfs4_client_t *cp; + rfs4_session_t *sp; + session41_create_t sca; + sequenceid4 stseq; + sequenceid4 agseq; + nfsstat4 status = NFS4_OK; + + DTRACE_NFSV4_2(op__create__session__start, + struct compound_state *, cs, + CREATE_SESSION4args*, args); + + /* + * A CREATE_SESSION request can be prefixed by OP_SEQUENCE. + * In this case, the newly created session has no relation + * to the sessid used for the OP_SEQUENCE. + */ + + /* + * Find the clientid + */ + cp = rfs4_findclient_by_id(args->csa_clientid, TRUE); + if (cp == NULL) { + status = NFS4ERR_STALE_CLIENTID; + goto out; + } + + /* + * Make sure the lease is still valid. + */ + if (rfs4_lease_expired(cp)) { + rfs4_client_close(cp); + status = NFS4ERR_STALE_CLIENTID; + goto out; + } + + /* + * Sequenceid processing (handling replay's, etc) + */ + agseq = args->csa_sequence; + stseq = cp->rc_contrived.xi_sid; + if (stseq == agseq + 1) { + /* + * If the previous sequenceid, then must be a replay of a + * previous CREATE_SESSION; return the cached result. + */ + crp = (CREATE_SESSION4resok *)&cp->rc_contrived.cs_res; + status = cp->rc_contrived.cs_status; + rok->csr_sequence = agseq; + bcopy(crp->csr_sessionid, rok->csr_sessionid, + sizeof (sessionid4)); + rok->csr_flags = crp->csr_flags; + rok->csr_fore_chan_attrs = crp->csr_fore_chan_attrs; + rok->csr_back_chan_attrs = crp->csr_back_chan_attrs; + + rfs4_update_lease(cp); + rfs4_client_rele(cp); + goto out; + } + + if (stseq != agseq) { + /* + * No way to differentiate MISORD_NEWREQ vs. MISORD_REPLAY, + * so anything else, we simply treat as SEQ_MISORDERED. + */ + status = NFS4ERR_SEQ_MISORDERED; + rfs4_client_rele(cp); + goto out; + } + + /* + * Clientid confirmation + */ + if (cp->rc_need_confirm) { + if (rfs4_cmp_cred_set(&cp->rc_cr_set, cs)) { + cp->rc_need_confirm = FALSE; + if (cp->rc_cp_confirmed != NULL) { + rfs4_client_close(cp->rc_cp_confirmed); + cp->rc_cp_confirmed = NULL; + } + } else { + status = NFS4ERR_CLID_INUSE; + rfs4_client_rele(cp); + goto out; + } + } + + /* + * Session creation + */ + sca.cs_error = 0; + sca.cs_req = req; + sca.cs_client = cp; + sca.cs_aotw = *args; + sp = rfs4x_createsession(&sca); + + if (sca.cs_error) { + status = sca.cs_error; + rfs4_client_rele(cp); + if (sp != NULL) + rfs4x_session_rele(sp); + goto out; + } + + if (sp == NULL) { + status = NFS4ERR_SERVERFAULT; + rfs4_client_rele(cp); + goto out; + } + + /* + * Need to store the result in the rfs4_client_t's contrived + * result slot and then respond from there. This way, when the + * csa_sequence == contrived.cc_sid, we can return the latest + * cached result. (see replay: above) + */ + crp = (CREATE_SESSION4resok *)&cp->rc_contrived.cs_res; + cp->rc_contrived.cs_status = NFS4_OK; + rok->csr_sequence = crp->csr_sequence = cp->rc_contrived.xi_sid; + bcopy(sp->sn_sessid, rok->csr_sessionid, sizeof (sessionid4)); + bcopy(sp->sn_sessid, crp->csr_sessionid, sizeof (sessionid4)); + rok->csr_flags = crp->csr_flags = sp->sn_csflags; + + cp->rc_contrived.xi_sid++; + + /* + * XXX: struct assignment of channel4_attrs is broken because + * ca_rdma_ird is specified as a single element array. A struct + * assignment will copy the ca_rdma_ird array ptr to multiple args/ + * res structs, and the ptr will be free'd multiple times. + * Without RDMA, ca_rdma_ird is a zero element array so its ptr + * is NULL (which is why this doesn't cause problems right now). + * + * Struct assignment is convenient, and it would be best to enable + * it by creating an in-kernel channel4_attrs struct which didn't + * contain the single element array, but just contained the inbound + * receive queue depth. Let the XDR encode/decode funcs convert + * from the in-kernel form to the OTW form. + */ + rok->csr_fore_chan_attrs = + crp->csr_fore_chan_attrs = sp->sn_fore->cn_attrs; + rok->csr_back_chan_attrs = crp->csr_back_chan_attrs = + args->csa_back_chan_attrs; + + rfs4_update_lease(cp); + + /* + * References from the session to the client are + * accounted for while session is being created. + */ + rfs4_client_rele(cp); + rfs4x_session_rele(sp); +out: + *cs->statusp = resp->csr_status = status; + + DTRACE_NFSV4_2(op__create__session__done, + struct compound_state *, cs, + CREATE_SESSION4res *, resp); +} + +/* ARGSUSED */ +void +rfs4x_op_destroy_session(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, compound_state_t *cs) +{ + DESTROY_SESSION4args *args = &argop->nfs_argop4_u.opdestroy_session; + DESTROY_SESSION4res *resp = &resop->nfs_resop4_u.opdestroy_session; + rfs4_session_t *sp; + rfs4_client_t *cp; + nfsstat4 status = NFS4_OK; + int addref = 0; /* additional reference */ + + DTRACE_NFSV4_2(op__destroy__session__start, + struct compound_state *, cs, + DESTROY_SESSION4args *, args); + + /* section 18.37.3 rfc5661 */ + if (rfs4_has_session(cs)) { + /* compound with a sequence */ + if (bcmp(args->dsa_sessionid, cs->sp->sn_sessid, + sizeof (sessionid4)) == 0) { + /* + * Same session. + * must be the final operation in the COMPOUND request + */ + if ((cs->op_pos + 1) != cs->op_len) { + status = NFS4ERR_NOT_ONLY_OP; + goto out; + } + addref++; + } else { + /* Not the same session */ + /* + * TODO: need to verify that both sessions share + * the connection + */ + DTRACE_PROBE(nfss41__i__destroy_encap_session); + + } + } + + sp = rfs4x_findsession_by_id(args->dsa_sessionid); + if (sp == NULL) { + status = NFS4ERR_BADSESSION; + goto out; + } + /* + * verify cred that was used to create the session matches and is in + * concordance w/the state protection type used. + */ + cp = sp->sn_clnt; + switch (cp->rc_state_prot.sp_type) { + case SP4_MACH_CRED: + cmn_err(CE_NOTE, "op_destroy_session: SP4_MACH_CRED"); + if (!rfs4_cmp_cred_set(&cp->rc_cr_set, cs)) { + status = NFS4ERR_PERM; + rfs4x_session_rele(sp); + goto out; + } + break; + + case SP4_SSV: + /* + * XXX - Need some of Karen's secret ssv sauce + * here. For now, just allow the destroy. + */ + cmn_err(CE_NOTE, "op_destroy_session: SP4_SSV"); + break; + + case SP4_NONE: + break; + + default: + break; + } + + status = rfs4x_destroysession(sp, 2 + addref); + rfs4x_session_rele(sp); +out: + *cs->statusp = resp->dsr_status = status; + + DTRACE_NFSV4_2(op__destroy__session__done, + struct compound_state *, cs, + DESTROY_SESSION4res *, resp); +} + +/* + * The thread will traverse the entire list pinging the connections + * that need it and refreshing any stale/dead connections. + */ +static void +ping_cb_null_thr(rfs4_session_t *sp) +{ + /* TODO: Need implementation */ + cmn_err(CE_NOTE, "nfs4x: ping_cb_null thread"); + rfs4x_session_rele(sp); + thread_exit(); +} + +/* + * Find session and validate sequence args. + * If this function successfully completes the compound state + * will contain a session pointer. + */ +static nfsstat4 +rfs4x_find_session(SEQUENCE4args *sargs, struct compound_state *cs) +{ + rfs4_session_t *sp; + slotid4 slot; + + ASSERT(sargs != NULL); + + if ((sp = rfs4x_findsession_by_id(sargs->sa_sessionid)) == NULL) + return (NFS4ERR_BADSESSION); + + slot = sargs->sa_slotid; + if (slot < 0 || slot >= sp->sn_fore->cn_attrs.ca_maxrequests) { + rfs4x_session_rele(sp); + return (NFS4ERR_BADSLOT); + } + cs->sp = sp; + cs->cachethis = sargs->sa_cachethis; + + return (NFS4_OK); +} + +/* called under held lock */ +static nfsstat4 +check_slot_seqid(rfs4_slot_t *slot, sequenceid4 seqid) +{ + nfsstat4 status = NFS4ERR_SEQ_MISORDERED; + + if (slot->se_flags & RFS4_SLOT_INUSE) { + /* + * There are three cases: + * 1. Duplicated requests for currently performing + * duplicated request. + * 2. New request for currently performing duplicated + * request. + * 3. Request with bad seqid for non finished performing + * request (due to a little window between 'prep' + * stage and actual renew se_seqid). + * In all cases tell a client to retry request later. + */ + if (slot->se_seqid == seqid || slot->se_seqid + 1 == seqid) { + status = NFS4ERR_DELAY; + } + } else { + if (seqid == slot->se_seqid + 1) + status = NFS4_OK; + else if (seqid == slot->se_seqid) + status = nfserr_replay_cache; + } + return (status); +} + +/* + * Prep stage for SEQUENCE operation. + * + * Main purpose to call this: + * - check on cached replay + * - Set cs.sp and cs.slot + */ +int +rfs4x_sequence_prep(COMPOUND4args *args, COMPOUND4res *resp, + compound_state_t *cs) +{ + SEQUENCE4args *sargs; + nfsstat4 status; + rfs4_slot_t *slot; + + if (args->array_len == 0 || args->array[0].argop != OP_SEQUENCE) + return (NFS4_OK); + + sargs = &args->array[0].nfs_argop4_u.opsequence; + + status = rfs4x_find_session(sargs, cs); + if (status != NFS4_OK) + return (status); + + /* have reference to session */ + slot = &cs->sp->sn_slots[sargs->sa_slotid]; + + mutex_enter(&slot->se_lock); + status = check_slot_seqid(slot, sargs->sa_sequenceid); + if (status == nfserr_replay_cache) { + if (slot->se_flags & RFS4_SLOT_CACHED) { + slot->se_flags |= RFS4_SLOT_INUSE; + cs->slot = slot; + *resp = slot->se_buf; + } else { + status = NFS4ERR_SEQ_MISORDERED; + } + } else if (status == NFS4_OK) { + slot->se_flags |= RFS4_SLOT_INUSE; + cs->slot = slot; + } + mutex_exit(&slot->se_lock); + + return (status); +} + +/* + * Do cleanup things + * 1. cache reply + * 2. release slot + */ +void +rfs4x_sequence_done(COMPOUND4res *resp, compound_state_t *cs) +{ + rfs4_slot_t *slot = cs->slot; + rfs4_session_t *sp = cs->sp; + int add = 0; + + ASSERT(slot != NULL); + ASSERT(sp != NULL); + + mutex_enter(&slot->se_lock); + slot->se_flags &= ~RFS4_SLOT_INUSE; + + if (*cs->statusp != nfserr_replay_cache) { + if (slot->se_flags & RFS4_SLOT_CACHED) { + rfs4_compound_free(&slot->se_buf); + slot->se_flags &= ~RFS4_SLOT_CACHED; + add = -1; + } + + if (*cs->statusp == NFS4_OK && cs->cachethis) { + slot->se_flags |= RFS4_SLOT_CACHED; + slot->se_buf = *resp; /* cache a reply */ + add += 1; + } else { + rfs4_compound_free(resp); + } + } + mutex_exit(&slot->se_lock); + + if (add != 0) + atomic_add_32(&sp->sn_rcached, add); +} + +/* + * Process the SEQUENCE operation. The session pointer has already been + * cached in the compound state, so we just dereference + */ +/*ARGSUSED*/ +void +rfs4x_op_sequence(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, compound_state_t *cs) +{ + SEQUENCE4args *args = &argop->nfs_argop4_u.opsequence; + SEQUENCE4res *resp = &resop->nfs_resop4_u.opsequence; + SEQUENCE4resok *rok = &resp->SEQUENCE4res_u.sr_resok4; + rfs4_session_t *sp = cs->sp; + rfs4_slot_t *slot = cs->slot; + nfsstat4 status = NFS4_OK; + uint32_t cbstat = 0x0; + + DTRACE_NFSV4_2(op__sequence__start, + struct compound_state *, cs, + SEQUENCE4args *, args); + + ASSERT(sp != NULL && slot != NULL); + + if (cs->op_pos != 0) { + status = NFS4ERR_SEQUENCE_POS; + goto out; + } + + if (rfs4_lease_expired(sp->sn_clnt)) { + status = NFS4ERR_BADSESSION; + goto out; + } + + /* + * If the back channel has been established... + * . if the channel has _not_ been marked as failed _AND_ + * there are connections that have pings outstanding, + * we go ahead and fire the thread to traverse all of + * the session's conns, issuing CB_NULL's to those that + * need a ping. + * . if the channel is _not_ OK (ie. failed), then notify + * client that there is currently a problem with the CB + * path. + */ + rfs4_dbe_lock(sp->sn_dbe); + if (SN_CB_CHAN_EST(sp)) { + if (SN_CB_CHAN_OK(sp)) { + if (sp->sn_bc.pngcnt > 0 && !sp->sn_bc.pnginprog) { + kthread_t *t; + + rfs4x_session_hold(sp); + t = thread_create(NULL, 0, ping_cb_null_thr, + sp, 0, &p0, TS_RUN, minclsyspri); + if (!t) + rfs4x_session_rele(sp); + } + } else { + cbstat |= SEQ4_STATUS_CB_PATH_DOWN; + } + } + cs->client = sp->sn_clnt; + + DTRACE_PROBE1(compound_clid, clientid4, cs->client->rc_clientid); + + ASSERT(args->sa_sequenceid == slot->se_seqid + 1); + + /* + * New request. + */ + mutex_enter(&slot->se_lock); + slot->se_seqid = args->sa_sequenceid; + mutex_exit(&slot->se_lock); + + /* + * Update access time and lease + */ + cs->slotno = args->sa_slotid; + sp->sn_laccess = nfs_sys_uptime(); + rfs4_update_lease(cs->client); + + /* + * Let's keep it simple for now + */ + bcopy(sp->sn_sessid, rok->sr_sessionid, sizeof (sessionid4)); + rok->sr_sequenceid = slot->se_seqid; + rok->sr_slotid = args->sa_slotid; + rok->sr_highest_slotid = + sp->sn_fore->cn_attrs.ca_maxrequests - 1; + rok->sr_target_highest_slotid = + sp->sn_fore->cn_attrs.ca_maxrequests - 1; + rok->sr_status_flags |= cbstat; + rfs4_dbe_unlock(sp->sn_dbe); +out: + *cs->statusp = resp->sr_status = status; + DTRACE_NFSV4_2(op__sequence__done, + struct compound_state *, cs, + SEQUENCE4res *, resp); +} + +/* ARGSUSED */ +void +rfs4x_op_reclaim_complete(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, compound_state_t *cs) +{ + RECLAIM_COMPLETE4args *args = &argop->nfs_argop4_u.opreclaim_complete; + RECLAIM_COMPLETE4res *resp = &resop->nfs_resop4_u.opreclaim_complete; + rfs4_client_t *cp; + nfsstat4 status = NFS4_OK; + + DTRACE_NFSV4_2(op__reclaim__complete__start, + struct compound_state *, cs, + RECLAIM_COMPLETE4args *, args); + + cp = cs->client; + rfs4_dbe_lock(cp->rc_dbe); + if (args->rca_one_fs) { + /* do what? we don't track this */ + goto out; + } + + if (cp->rc_reclaim_completed) { + status = NFS4ERR_COMPLETE_ALREADY; + goto out; + } + + if (cp->rc_can_reclaim) { + ASSERT(rfs4_servinst(cp)->nreclaim > 0); + atomic_add_32(&(rfs4_servinst(cp))->nreclaim, -1); + } + + cp->rc_reclaim_completed = 1; +out: + rfs4_dbe_unlock(cp->rc_dbe); + + *cs->statusp = resp->rcr_status = status; + DTRACE_NFSV4_2(op__reclaim__complete__done, + struct compound_state *, cs, + RECLAIM_COMPLETE4res *, resp); +} + +/* ARGSUSED */ +void +rfs4x_op_destroy_clientid(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, compound_state_t *cs) +{ + DESTROY_CLIENTID4args *args = &argop->nfs_argop4_u.opdestroy_clientid; + DESTROY_CLIENTID4res *resp = &resop->nfs_resop4_u.opdestroy_clientid; + rfs4_client_t *cp; + nfsstat4 status = NFS4_OK; + + DTRACE_NFSV4_2(op__destroy__clientid__start, + struct compound_state *, cs, + DESTROY_CLIENTID4args *, args); + + cp = rfs4_findclient_by_id(args->dca_clientid, TRUE); + if (cp == NULL) { + status = NFS4ERR_STALE_CLIENTID; + goto end; + } + + rfs4_dbe_lock(cp->rc_dbe); + if (client_has_state_locked(cp)) + status = NFS4ERR_CLIENTID_BUSY; + else + cp->rc_destroying = TRUE; + rfs4_dbe_unlock(cp->rc_dbe); + + if (status == NFS4_OK) + rfs4_client_close(cp); + else + rfs4_client_rele(cp); +end: + *cs->statusp = resp->dcr_status = status; + + DTRACE_NFSV4_2(op__destroy__clientid__done, + struct compound_state *, cs, + DESTROY_CLIENTID4res *, resp); +} + +/*ARGSUSED*/ +void +rfs4x_op_bind_conn_to_session(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, compound_state_t *cs) +{ + BIND_CONN_TO_SESSION4args *args = + &argop->nfs_argop4_u.opbind_conn_to_session; + BIND_CONN_TO_SESSION4res *resp = + &resop->nfs_resop4_u.opbind_conn_to_session; + BIND_CONN_TO_SESSION4resok *rok = + &resp->BIND_CONN_TO_SESSION4res_u.bctsr_resok4; + rfs4_session_t *sp; + nfsstat4 status = NFS4_OK; + + DTRACE_NFSV4_2(op__bind__conn__to__session__start, + struct compound_state *, cs, + BIND_CONN_TO_SESSION4args *, args); + + if (cs->op_pos != 0) { + status = NFS4ERR_NOT_ONLY_OP; + goto end; + } + + sp = rfs4x_findsession_by_id(args->bctsa_sessid); + if (sp == NULL) { + status = NFS4ERR_BADSESSION; + goto end; + } + + rfs4_update_lease(sp->sn_clnt); /* no need lock protection */ + + rfs4_dbe_lock(sp->sn_dbe); + sp->sn_laccess = nfs_sys_uptime(); + rfs4_dbe_unlock(sp->sn_dbe); + + rok->bctsr_use_conn_in_rdma_mode = FALSE; + + switch (args->bctsa_dir) { + case CDFC4_FORE: + case CDFC4_FORE_OR_BOTH: + /* always map to Fore */ + rok->bctsr_dir = CDFS4_FORE; + break; + + case CDFC4_BACK: + case CDFC4_BACK_OR_BOTH: + /* TODO: always map to Back */ + rok->bctsr_dir = CDFS4_FORE; + break; + default: + break; + } + + bcopy(sp->sn_sessid, rok->bctsr_sessid, sizeof (sessionid4)); + rfs4x_session_rele(sp); +end: + *cs->statusp = resp->bctsr_status = status; + + DTRACE_NFSV4_2(op__bind__conn__to__session__done, + struct compound_state *, cs, + BIND_CONN_TO_SESSION4res *, resp); +} + +/* ARGSUSED */ +void +rfs4x_op_secinfo_noname(nfs_argop4 *argop, nfs_resop4 *resop, + struct svc_req *req, compound_state_t *cs) +{ + SECINFO_NO_NAME4res *resp = &resop->nfs_resop4_u.opsecinfo_no_name; + nfsstat4 status; + bool_t dotdot; + + DTRACE_NFSV4_1(op__secinfo__no__name__start, + struct compound_state *, cs); + + if (cs->vp == NULL) { + status = NFS4ERR_NOFILEHANDLE; + goto out; + } + + if (cs->vp->v_type != VDIR) { + status = NFS4ERR_NOTDIR; + goto out; + } + + dotdot = + (argop->nfs_argop4_u.opsecinfo_no_name == SECINFO_STYLE4_PARENT); + + status = do_rfs4_op_secinfo(cs, dotdot ? ".." : ".", resp); + + /* Cleanup FH as described at 18.45.3 and 2.6.3.1.1.8 */ + if (status == NFS4_OK) { + VN_RELE(cs->vp); + cs->vp = NULL; + } +out: + *cs->statusp = resp->status = status; + + DTRACE_NFSV4_2(op__secinfo__no__name__done, + struct compound_state *, cs, + SECINFO_NO_NAME4res *, resp); +} diff --git a/usr/src/uts/common/fs/nfs/nfs4x_state.c b/usr/src/uts/common/fs/nfs/nfs4x_state.c new file mode 100644 index 0000000000..aba02734fc --- /dev/null +++ b/usr/src/uts/common/fs/nfs/nfs4x_state.c @@ -0,0 +1,576 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 RackTop Systems. + */ + +#include <sys/sdt.h> +#include <sys/atomic.h> +#include <nfs/nfs4.h> + +#ifdef DEBUG +#define RFS4_TABSIZE 17 +#else +#define RFS4_TABSIZE 2047 +#endif + +#define RFS4_MAXTABSZ 1024*1024 + +slotid4 rfs4_max_slots = MAXSLOTS; /* fore channel */ +slotid4 rfs4_back_max_slots = MAXSLOTS_BACK; /* back channel */ + +typedef union { + /* Both members have the same size */ + struct { + uint32_t pad0; + uint32_t pad1; + uint32_t start_time; /* NFS server start time */ + uint32_t s_id; /* unique session index */ + } impl_id; + sessionid4 id4; +} rfs4_sid; + +/* + * -------------------------------------------------------- + * MDS - NFSv4.1 Sessions + * -------------------------------------------------------- + */ +static uint32_t +sessid_hash(void *key) +{ + rfs4_sid *idp = key; + + return (idp->impl_id.s_id); +} + +static bool_t +sessid_compare(rfs4_entry_t entry, void *key) +{ + rfs4_session_t *sp = (rfs4_session_t *)entry; + sessionid4 *idp = (sessionid4 *)key; + + return (bcmp(idp, &sp->sn_sessid, sizeof (sessionid4)) == 0); +} + +static void * +sessid_mkkey(rfs4_entry_t entry) +{ + rfs4_session_t *sp = (rfs4_session_t *)entry; + + return (&sp->sn_sessid); +} + +/* ARGSUSED */ +static bool_t +cmp_false(rfs4_entry_t entry, void *key) +{ + return (FALSE); +} + +/* ARGSUSED */ +static void * +mkkey_null(rfs4_entry_t entry) +{ + return (NULL); +} + +void +rfs4x_session_rele(rfs4_session_t *sp) +{ + rfs4_dbe_rele(sp->sn_dbe); +} + +void +rfs4x_session_hold(rfs4_session_t *sp) +{ + rfs4_dbe_hold(sp->sn_dbe); +} + +rfs4_session_t * +rfs4x_findsession_by_id(sessionid4 sessid) +{ + rfs4_session_t *sp; + bool_t create = FALSE; + nfs4_srv_t *nsrv4 = nfs4_get_srv(); + + rw_enter(&nsrv4->findsession_lock, RW_READER); + sp = (rfs4_session_t *)rfs4_dbsearch(nsrv4->rfs4_session_idx, + sessid, &create, NULL, RFS4_DBS_VALID); + rw_exit(&nsrv4->findsession_lock); + + return (sp); +} + +/* + * A clientid can have multiple sessions associated with it. Hence, + * performing a raw 'mds_findsession' (even for a create) might + * yield a list of sessions associated with the clientid in question. + * Call rfs4_dbseach() function with key that cannot be found + * and create an association between the session table and both + * primary (sessionid) index and secondary (clientid) index for the + * newly created session. + */ + +rfs4_session_t * +rfs4x_createsession(session41_create_t *ap) +{ + static volatile uint32_t session_id_counter; + + rfs4_session_t *sp = NULL; + bool_t create = TRUE; + rfs4_sid key = {0, 0, 0, 0}; + nfs4_srv_t *nsrv4 = nfs4_get_srv(); + + /* + * Use unique counter for s_id and s_id to ensure that + * created entry will have the same index in dbi_buckets[] + */ + ap->cs_id = key.impl_id.s_id = atomic_inc_32_nv(&session_id_counter); + + rw_enter(&nsrv4->findsession_lock, RW_WRITER); + if ((sp = (rfs4_session_t *)rfs4_dbsearch(nsrv4->rfs4_session_idx, + &key, &create, (void *)ap, RFS4_DBS_VALID)) == NULL) { + DTRACE_PROBE1(mds__srv__createsession__fail, + session41_create_t *, ap); + } + rw_exit(&nsrv4->findsession_lock); + return (sp); +} + +/* return success of operation */ +static bool_t +client_insert_session(rfs4_client_t *cp, rfs4_session_t *sp) +{ + bool_t res = TRUE; + + rfs4_dbe_lock(cp->rc_dbe); + if (cp->rc_destroying) + res = FALSE; + else + list_insert_tail(&cp->rc_sessions, sp); + rfs4_dbe_unlock(cp->rc_dbe); + + return (res); +} + +static void +client_remove_session(rfs4_client_t *cp, rfs4_session_t *sp) +{ + rfs4_dbe_lock(cp->rc_dbe); + if (list_link_active(&sp->sn_node)) + list_remove(&cp->rc_sessions, sp); + rfs4_dbe_unlock(cp->rc_dbe); +} + +/* + * Invalidate the session in the DB (so it can't be found anymore) + */ +nfsstat4 +rfs4x_destroysession(rfs4_session_t *sp, unsigned useref) +{ + nfsstat4 status = NFS4_OK; + + /* + * RFC 7862 Section 14.1.3: + * In hindsight, the NFSv4.1 specification should have + * mandated that DESTROY_SESSION either abort or complete + * all outstanding operations. + */ + rfs4_dbe_lock(sp->sn_dbe); + if (rfs4_dbe_refcnt(sp->sn_dbe) > useref) + status = NFS4ERR_DELAY; + else + rfs4_dbe_invalidate(sp->sn_dbe); + rfs4_dbe_unlock(sp->sn_dbe); + + if (status == NFS4_OK) + client_remove_session(sp->sn_clnt, sp); + + return (status); +} + +/* Invalidate all client's sessions */ +void +rfs4x_client_session_remove(rfs4_client_t *cp) +{ + rfs4_session_t *sp; + + /* + * Client is forcibly closing so invalidate all sessions + * without checking the refcount. + */ + rfs4_dbe_lock(cp->rc_dbe); + while ((sp = list_head(&cp->rc_sessions)) != NULL) { + list_remove(&cp->rc_sessions, sp); + + rfs4_dbe_lock(sp->sn_dbe); + rfs4_dbe_invalidate(sp->sn_dbe); + rfs4_dbe_unlock(sp->sn_dbe); + + } + rfs4_dbe_unlock(cp->rc_dbe); +} + +nfsstat4 +sess_chan_limits(sess_channel_t *scp) +{ + if (scp->cn_attrs.ca_maxrequests > rfs4_max_slots) { + scp->cn_attrs.ca_maxrequests = rfs4_max_slots; + } + + if (scp->cn_back_attrs.ca_maxrequests > rfs4_back_max_slots) + scp->cn_back_attrs.ca_maxrequests = rfs4_back_max_slots; + + + if (scp->cn_attrs.ca_maxoperations > NFS4_COMPOUND_LIMIT) + scp->cn_attrs.ca_maxoperations = NFS4_COMPOUND_LIMIT; + + /* + * Lower limit should be set to smallest sane COMPOUND. Even + * though a singleton SEQUENCE op is the very smallest COMPOUND, + * it's also quite boring. For all practical purposes, the lower + * limit for creating a sess is limited to: + * + * [SEQUENCE + PUTROOTFH + GETFH] + * + * XXX - can't limit READ's to a specific threshold, otherwise + * we artificially limit the clients to perform reads of + * AT LEAST that granularity, which is WRONG !!! Same goes + * for READDIR's and GETATTR's. + */ + if (scp->cn_attrs.ca_maxresponsesize < (sizeof (SEQUENCE4res) + + sizeof (PUTROOTFH4res) + sizeof (GETFH4res))) + return (NFS4ERR_TOOSMALL); + return (NFS4_OK); +} + +/* + * NFSv4.1 Slot replay cache + */ +static void +rfs41_cleanup_slot(rfs4_slot_t *se) +{ + rfs4_compound_free((COMPOUND4res *)&se->se_buf); +} + +static rfs4_slot_t * +slots_alloc(size_t n) +{ + rfs4_slot_t *p; + int i; + + p = kmem_zalloc(sizeof (rfs4_slot_t) * n, KM_SLEEP); + for (i = 0; i < n; i++) { + mutex_init(&p[i].se_lock, NULL, MUTEX_DEFAULT, NULL); + } + + return (p); +} + +static void +slots_free(rfs4_slot_t *slots, size_t n) +{ + int i; + + for (i = 0; i < n; i++) { + rfs4_slot_t *slot = &slots[i]; + + mutex_destroy(&slot->se_lock); + + if (slot->se_flags & RFS4_SLOT_CACHED) { + rfs41_cleanup_slot(slot); + } + } + kmem_free(slots, sizeof (rfs4_slot_t) * n); +} + +/* Additional functions */ + +/* check csa_flags for OP_CREATE_SESSION */ +bool_t +nfs4x_csa_flags_valid(uint32_t flags) +{ + if (flags & ~CREATE_SESSION4_FLAG_MASK) + return (FALSE); + + return (TRUE); +} + +sess_channel_t * +rfs41_create_session_channel(channel_dir_from_server4 dir) +{ + sess_channel_t *cp; + sess_bcsd_t *bp; + + cp = (sess_channel_t *)kmem_zalloc(sizeof (sess_channel_t), KM_SLEEP); + rw_init(&cp->cn_lock, NULL, RW_DEFAULT, NULL); + + switch (dir) { + case CDFS4_FORE: + break; + + case CDFS4_BOTH: + case CDFS4_BACK: + /* BackChan Specific Data */ + bp = (sess_bcsd_t *)kmem_zalloc(sizeof (sess_bcsd_t), KM_SLEEP); + rw_init(&bp->bsd_rwlock, NULL, RW_DEFAULT, NULL); + cp->cn_csd = (sess_bcsd_t *)bp; + break; + } + return (cp); +} + +void +rfs41_destroy_session_channel(rfs4_session_t *sp) +{ + sess_channel_t *cp; + sess_bcsd_t *bp; + + if (sp->sn_back != NULL) { + /* only one channel for both direction for now */ + ASSERT(sp->sn_fore == sp->sn_back); + + cp = sp->sn_back; + bp = (sess_bcsd_t *)cp->cn_csd; + rw_destroy(&bp->bsd_rwlock); + kmem_free(bp, sizeof (sess_bcsd_t)); + } else { + cp = sp->sn_fore; + } + + rw_destroy(&cp->cn_lock); + kmem_free(cp, sizeof (sess_channel_t)); + + sp->sn_back = NULL; + sp->sn_fore = NULL; +} + +static bool_t +rfs4_session_create(rfs4_entry_t u_entry, void *arg) +{ + rfs4_session_t *sp = (rfs4_session_t *)u_entry; + session41_create_t *ap = (session41_create_t *)arg; + sess_channel_t *ocp = NULL; + rfs4_sid *sidp; + bool_t bdrpc = FALSE; + channel_dir_from_server4 dir; + nfsstat4 sle; + nfs4_srv_t *nsrv4 = nfs4_get_srv(); + + ASSERT(sp != NULL); + if (sp == NULL) + return (FALSE); + + /* + * Back pointer/ref to parent data struct (rfs4_client_t) + */ + sp->sn_clnt = (rfs4_client_t *)ap->cs_client; + rfs4_dbe_hold(sp->sn_clnt->rc_dbe); + + /* + * Handcrafting the session id + */ + sidp = (rfs4_sid *)&sp->sn_sessid; + sidp->impl_id.pad0 = 0x00000000; + sidp->impl_id.pad1 = 0xFFFFFFFF; + sidp->impl_id.start_time = nsrv4->rfs4_start_time; + sidp->impl_id.s_id = ap->cs_id; + + /* + * Process csa_flags; note that CREATE_SESSION4_FLAG_CONN_BACK_CHAN + * is processed below since it affects direction and setup of the + * backchannel accordingly. + */ + if (!nfs4x_csa_flags_valid(ap->cs_aotw.csa_flags)) { + ap->cs_error = NFS4ERR_INVAL; + goto err; + } + + sp->sn_csflags = 0; + if (ap->cs_aotw.csa_flags & CREATE_SESSION4_FLAG_PERSIST) + /* XXX - Worry about persistence later */ + sp->sn_csflags &= ~CREATE_SESSION4_FLAG_PERSIST; + + if (ap->cs_aotw.csa_flags & CREATE_SESSION4_FLAG_CONN_RDMA) + /* XXX - No RDMA for now */ + sp->sn_csflags &= ~CREATE_SESSION4_FLAG_CONN_RDMA; + + /* + * Initialize some overall sessions values + */ + sp->sn_bc.progno = ap->cs_aotw.csa_cb_program; + sp->sn_laccess = nfs_sys_uptime(); + sp->sn_flags = 0; + sp->sn_rcached = 0; + + /* + * Check if client has specified that the FORE channel should + * also be used for call back traffic (ie. bidir RPC). If so, + * let's try to accomodate the request. + */ + DTRACE_PROBE1(csa__flags, uint32_t, ap->cs_aotw.csa_flags); + + /* + * Session's channel flags depending on bdrpc + * TODO: Add backchannel handling, i.e. when bdrpc is TRUE + */ + dir = bdrpc ? (CDFS4_FORE | CDFS4_BACK) : CDFS4_FORE; + ocp = rfs41_create_session_channel(dir); + ocp->cn_dir = dir; + sp->sn_fore = ocp; + + /* + * Check if channel attrs will be flexible enough for future + * purposes. Channel attribute enforcement is done as part of + * COMPOUND processing. + */ + ocp->cn_attrs = ap->cs_aotw.csa_fore_chan_attrs; + ocp->cn_back_attrs = ap->cs_aotw.csa_back_chan_attrs; + if (sle = sess_chan_limits(ocp)) { + ap->cs_error = sle; + goto err_free_chan; + } + + /* will fail if client is going to destroy */ + if (!client_insert_session(sp->sn_clnt, sp)) { + ap->cs_error = NFS4ERR_DELAY; + goto err_free_chan; + } + + /* + * No need for locks/synchronization at this time, + * since we're barely creating the session. + */ + if (bdrpc) { + /* Need to be implemented */ + VERIFY(0); + } else { + /* + * If not doing bdrpc, then we expect the client to perform + * an explicit BIND_CONN_TO_SESSION if it wants callback + * traffic. Subsequently, the cb channel should be set up + * at that point along with its corresponding slot (see + * rfs41_bc_setup). + */ + sp->sn_csflags &= ~CREATE_SESSION4_FLAG_CONN_BACK_CHAN; + sp->sn_back = NULL; + + /* + * XXX 08/15/2008 (rick) - if the channel is not bidir when + * created in CREATE_SESSION, then we should save off + * the ap->cs_aotw.csa_back_chan_attrs in case later + * a bc2s is called to create the back channel. + */ + } + + /* + * Now we allocate space for the slrc, initializing each slot's + * sequenceid and slotid to zero and a (pre)cached result of + * NFS4ERR_SEQ_MISORDERED. Note that we zero out the entries + * by virtue of the z-alloc. + */ + sp->sn_slots = slots_alloc(ocp->cn_attrs.ca_maxrequests); + + return (TRUE); + +err_free_chan: + rfs41_destroy_session_channel(sp); +err: + rfs4_dbe_rele(sp->sn_clnt->rc_dbe); + return (FALSE); +} + +static void +rfs4_session_destroy(rfs4_entry_t u_entry) +{ + rfs4_session_t *sp = (rfs4_session_t *)u_entry; + sess_bcsd_t *bsdp; + + if (SN_CB_CHAN_EST(sp) && (bsdp = sp->sn_back->cn_csd) != NULL) { + slots_free(bsdp->bsd_slots, + sp->sn_back->cn_back_attrs.ca_maxrequests); + bsdp->bsd_slots = NULL; + } + + /* + * Nuke slot replay cache for this session + */ + if (sp->sn_slots) { + slots_free(sp->sn_slots, sp->sn_fore->cn_attrs.ca_maxrequests); + sp->sn_slots = NULL; + } + + /* + * XXX - A session can have multiple BC clnt handles that need + * to be discarded. rfs4_session_inval calls CLNT_DESTROY + * which will remove the CB client handle from the global + * list (cb_clnt_list) now. This will have to change once + * we manage the BC clnt handles per session. + */ + + /* + * Remove the fore and back channels. + */ + rfs41_destroy_session_channel(sp); + + client_remove_session(sp->sn_clnt, sp); + + rfs4_client_rele(sp->sn_clnt); +} + +static bool_t +rfs4_session_expiry(rfs4_entry_t u_entry) +{ + rfs4_session_t *sp = (rfs4_session_t *)u_entry; + + if (sp == NULL || rfs4_dbe_is_invalid(sp->sn_dbe)) + return (TRUE); + + if (rfs4_lease_expired(sp->sn_clnt)) + return (TRUE); + + return (FALSE); +} + +void +rfs4x_state_init_locked(nfs4_srv_t *nsrv4) +{ + rw_init(&nsrv4->findsession_lock, NULL, RW_DEFAULT, NULL); + + nsrv4->rfs4_session_tab = rfs4_table_create(nsrv4->nfs4_server_state, + "Session", 5 * rfs4_lease_time, 1, rfs4_session_create, + rfs4_session_destroy, rfs4_session_expiry, sizeof (rfs4_session_t), + RFS4_TABSIZE, RFS4_MAXTABSZ/8, 100); + + nsrv4->rfs4_session_idx = rfs4_index_create(nsrv4->rfs4_session_tab, + "session_idx", sessid_hash, sessid_compare, sessid_mkkey, TRUE); +} + +void +rfs4x_state_fini(nfs4_srv_t *nsrv4) +{ + /* All tables will be destroyed by caller */ + rw_destroy(&nsrv4->findsession_lock); +} diff --git a/usr/src/uts/common/fs/nfs/nfs4x_xdr.c b/usr/src/uts/common/fs/nfs/nfs4x_xdr.c new file mode 100644 index 0000000000..7b4bce31f8 --- /dev/null +++ b/usr/src/uts/common/fs/nfs/nfs4x_xdr.c @@ -0,0 +1,2935 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2017 RackTop Systems. + */ + +/* + * This file contains the xdr for datatypes and operations that are + * specific to NFSv4 minor version 1 (i.e., datatypes that + * were either introduced or changed by the NFSv4.1 specification). + */ + +/* + * INSTRUCTIONS for updating to a new NFSv4.1: + * + * **DO NOT** simply replace this file with code rpcgen creates + * and then remove non-NFS41 code. The file now contains the hand + * coded xdr for the NFS4.1 attributes. If you run rpcgen, + * manually integrate parts related to the draft diffs. + */ +#include <sys/statvfs.h> +#include <sys/sysmacros.h> +#include <sys/sdt.h> +#include <nfs/nfs4.h> +#include <nfs/nfs4_attr.h> + +#ifndef _KERNEL +#include <stdlib.h> +#endif /* !_KERNEL */ + +/* modified version */ +bool_t +xdr_verifier4(XDR *xdrs, verifier4 *objp) +{ + if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_sequenceid4(XDR *xdrs, sequenceid4 *objp) +{ + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_sessionid4(XDR *xdrs, sessionid4 objp) +{ + if (!xdr_opaque(xdrs, objp, NFS4_SESSIONID_SIZE)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_slotid4(XDR *xdrs, slotid4 *objp) +{ + + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_change_policy4(XDR *xdrs, change_policy4 *objp) +{ + if (!xdr_uint64_t(xdrs, &objp->cp_major)) + return (FALSE); + if (!xdr_uint64_t(xdrs, &objp->cp_minor)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_aclflag4(XDR *xdrs, aclflag4 *objp) +{ + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfsacl41(XDR *xdrs, nfsacl41 *objp) +{ + if (!xdr_aclflag4(xdrs, &objp->na41_flag)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->na41_aces.na41_aces_val, + (uint_t *)&objp->na41_aces.na41_aces_len, ~0, sizeof (nfsace4), + (xdrproc_t)xdr_nfsace4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_mode_masked4(XDR *xdrs, mode_masked4 *objp) +{ + if (!xdr_mode4(xdrs, &objp->mm_value_to_set)) + return (FALSE); + if (!xdr_mode4(xdrs, &objp->mm_mask_bits)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfs_impl_id4(XDR *xdrs, nfs_impl_id4 *objp) +{ + if (!xdr_utf8str_cis(xdrs, &objp->nii_domain)) + return (FALSE); + if (!xdr_utf8str_cs(xdrs, &objp->nii_name)) + return (FALSE); + if (!xdr_nfstime4(xdrs, &objp->nii_date)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_layouttype4(XDR *xdrs, layouttype4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_layout_content4(XDR *xdrs, layout_content4 *objp) +{ + if (!xdr_layouttype4(xdrs, &objp->loc_type)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->loc_body.loc_body_val, + (uint_t *)&objp->loc_body.loc_body_len, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_layoutiomode4(XDR *xdrs, layoutiomode4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + + +bool_t +xdr_layout4(XDR *xdrs, layout4 *objp) +{ + if (!xdr_offset4(xdrs, &objp->lo_offset)) + return (FALSE); + if (!xdr_length4(xdrs, &objp->lo_length)) + return (FALSE); + if (!xdr_layoutiomode4(xdrs, &objp->lo_iomode)) + return (FALSE); + if (!xdr_layout_content4(xdrs, &objp->lo_content)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_deviceid4(XDR *xdrs, deviceid4 objp) +{ + if (!xdr_opaque(xdrs, objp, NFS4_DEVICEID4_SIZE)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_device_addr4(XDR *xdrs, device_addr4 *objp) +{ + if (!xdr_layouttype4(xdrs, &objp->da_layout_type)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->da_addr_body.da_addr_body_val, + (uint_t *)&objp->da_addr_body.da_addr_body_len, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_layoutupdate4(XDR *xdrs, layoutupdate4 *objp) +{ + if (!xdr_layouttype4(xdrs, &objp->lou_type)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->lou_body.lou_body_val, + (uint_t *)&objp->lou_body.lou_body_len, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_layoutreturn_type4(XDR *xdrs, layoutreturn_type4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} +/* layouttype4 specific data */ + +bool_t +xdr_layoutreturn_file4(XDR *xdrs, layoutreturn_file4 *objp) +{ + if (!xdr_offset4(xdrs, &objp->lrf_offset)) + return (FALSE); + if (!xdr_length4(xdrs, &objp->lrf_length)) + return (FALSE); + if (!xdr_stateid4(xdrs, &objp->lrf_stateid)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->lrf_body.lrf_body_val, + (uint_t *)&objp->lrf_body.lrf_body_len, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_layoutreturn4(XDR *xdrs, layoutreturn4 *objp) +{ + if (!xdr_layoutreturn_type4(xdrs, &objp->lr_returntype)) + return (FALSE); + switch (objp->lr_returntype) { + case LAYOUTRETURN4_FILE: + if (!xdr_layoutreturn_file4(xdrs, + &objp->layoutreturn4_u.lr_layout)) + return (FALSE); + break; + } + return (TRUE); +} + + +bool_t +xdr_fs4_status_type(XDR *xdrs, fs4_status_type *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fs4_status(XDR *xdrs, fs4_status *objp) +{ + if (!xdr_bool(xdrs, &objp->fss_absent)) + return (FALSE); + if (!xdr_fs4_status_type(xdrs, &objp->fss_type)) + return (FALSE); + if (!xdr_utf8str_cs(xdrs, &objp->fss_source)) + return (FALSE); + if (!xdr_utf8str_cs(xdrs, &objp->fss_current)) + return (FALSE); + if (!xdr_int32_t(xdrs, &objp->fss_age)) + return (FALSE); + if (!xdr_nfstime4(xdrs, &objp->fss_version)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_threshold4_read_size(XDR *xdrs, threshold4_read_size *objp) +{ + + if (!xdr_length4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_threshold4_write_size(XDR *xdrs, threshold4_write_size *objp) +{ + if (!xdr_length4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_threshold4_read_iosize(XDR *xdrs, threshold4_read_iosize *objp) +{ + if (!xdr_length4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_threshold4_write_iosize(XDR *xdrs, threshold4_write_iosize *objp) +{ + if (!xdr_length4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_threshold_item4(XDR *xdrs, threshold_item4 *objp) +{ + if (!xdr_layouttype4(xdrs, &objp->thi_layout_type)) + return (FALSE); + if (!xdr_bitmap4(xdrs, &objp->thi_hintset)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->thi_hintlist.thi_hintlist_val, + (uint_t *)&objp->thi_hintlist.thi_hintlist_len, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_mdsthreshold4(XDR *xdrs, mdsthreshold4 *objp) +{ + if (!xdr_array(xdrs, (char **)&objp->mth_hints.mth_hints_val, + (uint_t *)&objp->mth_hints.mth_hints_len, ~0, + sizeof (threshold_item4), (xdrproc_t)xdr_threshold_item4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_retention_get4(XDR *xdrs, retention_get4 *objp) +{ + if (!xdr_uint64_t(xdrs, &objp->rg_duration)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->rg_begin_time.rg_begin_time_val, + (uint_t *)&objp->rg_begin_time.rg_begin_time_len, 1, + sizeof (nfstime4), (xdrproc_t)xdr_nfstime4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_retention_set4(XDR *xdrs, retention_set4 *objp) +{ + if (!xdr_bool(xdrs, &objp->rs_enable)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->rs_duration.rs_duration_val, + (uint_t *)&objp->rs_duration.rs_duration_len, 1, sizeof (uint64_t), + (xdrproc_t)xdr_uint64_t)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fs_charset_cap4(XDR *xdrs, fs_charset_cap4 *objp) +{ + + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_mode_set_masked(XDR *xdrs, fattr4_mode_set_masked *objp) +{ + if (!xdr_mode_masked4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_dir_notif_delay(XDR *xdrs, fattr4_dir_notif_delay *objp) +{ + + if (!xdr_nfstime4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_dirent_notif_delay(XDR *xdrs, fattr4_dirent_notif_delay *objp) +{ + if (!xdr_nfstime4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_fs_layout_types(XDR *xdrs, fattr4_fs_layout_types *objp) +{ + if (!xdr_array(xdrs, (char **)&objp->fattr4_fs_layout_types_val, + (uint_t *)&objp->fattr4_fs_layout_types_len, ~0, + sizeof (layouttype4), (xdrproc_t)xdr_layouttype4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_fs_status(XDR *xdrs, fattr4_fs_status *objp) +{ + if (!xdr_fs4_status(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_fs_charset_cap4(XDR *xdrs, fattr4_fs_charset_cap *objp) +{ + if (!xdr_fs_charset_cap4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_retention_get(XDR *xdrs, fattr4_retention_get *objp) +{ + if (!xdr_retention_get4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_retention_set(XDR *xdrs, fattr4_retention_set *objp) +{ + if (!xdr_retention_set4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_retentevt_get(XDR *xdrs, fattr4_retentevt_get *objp) +{ + if (!xdr_retention_get4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_retentevt_set(XDR *xdrs, fattr4_retentevt_set *objp) +{ + if (!xdr_retention_set4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_retention_hold(XDR *xdrs, fattr4_retention_hold *objp) +{ + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_dacl(XDR *xdrs, fattr4_dacl *objp) +{ + if (!xdr_nfsacl41(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_sacl(XDR *xdrs, fattr4_sacl *objp) +{ + if (!xdr_nfsacl41(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_client_owner4(XDR *xdrs, client_owner4 *objp) +{ + if (!xdr_verifier4(xdrs, (verifier4 *)&objp->co_verifier)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->co_ownerid.co_ownerid_val, + (uint_t *)&objp->co_ownerid.co_ownerid_len, NFS4_OPAQUE_LIMIT)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_server_owner4(XDR *xdrs, server_owner4 *objp) +{ + if (!xdr_uint64_t(xdrs, &objp->so_minor_id)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->so_major_id.so_major_id_val, + (uint_t *)&objp->so_major_id.so_major_id_len, NFS4_OPAQUE_LIMIT)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_state_owner4(XDR *xdrs, state_owner4 *objp) +{ + if (!xdr_clientid4(xdrs, &objp->clientid)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->owner.owner_val, + (uint_t *)&objp->owner.owner_len, NFS4_OPAQUE_LIMIT)) + return (FALSE); + return (TRUE); +} + +/* Input for computing subkeys */ + +bool_t +xdr_ssv_subkey4(XDR *xdrs, ssv_subkey4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + + +/* Input for computing smt_hmac */ + +bool_t +xdr_ssv_mic_plain_tkn4(XDR *xdrs, ssv_mic_plain_tkn4 *objp) +{ + if (!xdr_uint32_t(xdrs, &objp->smpt_ssv_seq)) + return (FALSE); + if (!xdr_bytes(xdrs, + (char **)&objp->smpt_orig_plain.smpt_orig_plain_val, + (uint_t *)&objp->smpt_orig_plain.smpt_orig_plain_len, ~0)) + return (FALSE); + return (TRUE); +} + + +/* SSV GSS PerMsgToken token */ + +bool_t +xdr_ssv_mic_tkn4(XDR *xdrs, ssv_mic_tkn4 *objp) +{ + if (!xdr_uint32_t(xdrs, &objp->smt_ssv_seq)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->smt_hmac.smt_hmac_val, + (uint_t *)&objp->smt_hmac.smt_hmac_len, ~0)) + return (FALSE); + return (TRUE); +} + + +/* Input for computing ssct_encr_data and ssct_hmac */ + +bool_t +xdr_ssv_seal_plain_tkn4(XDR *xdrs, ssv_seal_plain_tkn4 *objp) +{ + if (!xdr_bytes(xdrs, + (char **)&objp->sspt_confounder.sspt_confounder_val, + (uint_t *)&objp->sspt_confounder.sspt_confounder_len, ~0)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->sspt_ssv_seq)) + return (FALSE); + if (!xdr_bytes(xdrs, + (char **)&objp->sspt_orig_plain.sspt_orig_plain_val, + (uint_t *)&objp->sspt_orig_plain.sspt_orig_plain_len, ~0)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->sspt_pad.sspt_pad_val, + (uint_t *)&objp->sspt_pad.sspt_pad_len, ~0)) + return (FALSE); + return (TRUE); +} + + +/* SSV GSS SealedMessage token */ + +bool_t +xdr_ssv_seal_cipher_tkn4(XDR *xdrs, ssv_seal_cipher_tkn4 *objp) +{ + if (!xdr_uint32_t(xdrs, &objp->ssct_ssv_seq)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->ssct_iv.ssct_iv_val, + (uint_t *)&objp->ssct_iv.ssct_iv_len, ~0)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->ssct_encr_data.ssct_encr_data_val, + (uint_t *)&objp->ssct_encr_data.ssct_encr_data_len, ~0)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->ssct_hmac.ssct_hmac_val, + (uint_t *)&objp->ssct_hmac.ssct_hmac_len, ~0)) + return (FALSE); + return (TRUE); +} + + +bool_t +xdr_fs_locations_server4(XDR *xdrs, fs_locations_server4 *objp) +{ + if (!xdr_int32_t(xdrs, &objp->fls_currency)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->fls_info.fls_info_val, + (uint_t *)&objp->fls_info.fls_info_len, ~0)) + return (FALSE); + if (!xdr_utf8str_cis(xdrs, &objp->fls_server)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fs_locations_item4(XDR *xdrs, fs_locations_item4 *objp) +{ + if (!xdr_array(xdrs, (char **)&objp->fli_entries.fli_entries_val, + (uint_t *)&objp->fli_entries.fli_entries_len, ~0, + sizeof (fs_locations_server4), (xdrproc_t)xdr_fs_locations_server4)) + return (FALSE); + if (!xdr_pathname4(xdrs, &objp->fli_rootpath)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fs_locations_info4(XDR *xdrs, fs_locations_info4 *objp) +{ + if (!xdr_uint32_t(xdrs, &objp->fli_flags)) + return (FALSE); + if (!xdr_int32_t(xdrs, &objp->fli_valid_for)) + return (FALSE); + if (!xdr_pathname4(xdrs, &objp->fli_fs_root)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->fli_items.fli_items_val, + (uint_t *)&objp->fli_items.fli_items_len, ~0, + sizeof (fs_locations_item4), (xdrproc_t)xdr_fs_locations_item4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fattr4_fs_locations_info(XDR *xdrs, fattr4_fs_locations_info *objp) +{ + if (!xdr_fs_locations_info4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfl_util4(XDR *xdrs, nfl_util4 *objp) +{ + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_filelayout_hint_care4(XDR *xdrs, filelayout_hint_care4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + + +bool_t +xdr_multipath_list4(XDR *xdrs, multipath_list4 *objp) +{ + if (!xdr_array(xdrs, (char **)&objp->multipath_list4_val, + (uint_t *)&objp->multipath_list4_len, ~0, + sizeof (netaddr4), (xdrproc_t)xdr_netaddr4)) + return (FALSE); + return (TRUE); +} + +/* Encoded in the da_addr_body field of type device_addr4: */ + +bool_t +xdr_nfsv4_1_file_layout_ds_addr4(XDR *xdrs, nfsv4_1_file_layout_ds_addr4 *objp) +{ + if (!xdr_array(xdrs, + (char **)&objp->nflda_stripe_indices.nflda_stripe_indices_val, + (uint_t *)&objp->nflda_stripe_indices.nflda_stripe_indices_len, ~0, + sizeof (uint32_t), (xdrproc_t)xdr_uint32_t)) + return (FALSE); + if (!xdr_array(xdrs, + (char **)&objp->nflda_multipath_ds_list.nflda_multipath_ds_list_val, + (uint_t *)&objp->nflda_multipath_ds_list. + nflda_multipath_ds_list_len, ~0, sizeof (multipath_list4), + (xdrproc_t)xdr_multipath_list4)) + return (FALSE); + return (TRUE); +} + + +/* Encoded in the loc_body field of type layout_content4: */ + +bool_t +xdr_nfsv4_1_file_layout4(XDR *xdrs, nfsv4_1_file_layout4 *objp) +{ + if (!xdr_deviceid4(xdrs, objp->nfl_deviceid)) + return (FALSE); + if (!xdr_nfl_util4(xdrs, &objp->nfl_util)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->nfl_first_stripe_index)) + return (FALSE); + if (!xdr_offset4(xdrs, &objp->nfl_pattern_offset)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->nfl_fh_list.nfl_fh_list_val, + (uint_t *)&objp->nfl_fh_list.nfl_fh_list_len, ~0, + sizeof (nfs_fh4), (xdrproc_t)xdr_nfs_fh4)) + return (FALSE); + return (TRUE); +} + +/* + * Encoded in the lou_body field of type layoutupdate4: + * Nothing. lou_body is a zero length array of octets. + */ + + +bool_t +xdr_creatverfattr(XDR *xdrs, creatverfattr *objp) +{ + if (!xdr_verifier4(xdrs, (verifier4 *)&objp->cva_verf)) + return (FALSE); + if (!xdr_fattr4(xdrs, &objp->cva_attrs)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_open_claim_delegate_cur4(XDR *xdrs, open_claim_delegate_cur4 *objp) +{ + if (!xdr_stateid4(xdrs, &objp->delegate_stateid)) + return (FALSE); + if (!xdr_component4(xdrs, &objp->file)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_open_claim4(XDR *xdrs, open_claim4 *objp) +{ + if (!xdr_open_claim_type4(xdrs, &objp->claim)) + return (FALSE); + switch (objp->claim) { + case CLAIM_NULL: + if (!xdr_component4(xdrs, &objp->open_claim4_u.file)) + return (FALSE); + break; + case CLAIM_PREVIOUS: + if (!xdr_open_delegation_type4(xdrs, + &objp->open_claim4_u.delegate_type)) + return (FALSE); + break; + case CLAIM_DELEGATE_CUR: + if (!xdr_open_claim_delegate_cur4(xdrs, + &objp->open_claim4_u.delegate_cur_info)) + return (FALSE); + break; + case CLAIM_DELEGATE_PREV: + if (!xdr_component4(xdrs, + &objp->open_claim4_u.file_delegate_prev)) + return (FALSE); + break; + case CLAIM_FH: + break; + case CLAIM_DELEG_PREV_FH: + break; + case CLAIM_DELEG_CUR_FH: + if (!xdr_stateid4(xdrs, + &objp->open_claim4_u.oc_delegate_stateid)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_why_no_delegation4(XDR *xdrs, why_no_delegation4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_open_none_delegation4(XDR *xdrs, open_none_delegation4 *objp) +{ + if (!xdr_why_no_delegation4(xdrs, &objp->ond_why)) + return (FALSE); + switch (objp->ond_why) { + case WND4_CONTENTION: + if (!xdr_bool(xdrs, + &objp->open_none_delegation4_u.ond_server_will_push_deleg)) + return (FALSE); + break; + case WND4_RESOURCE: + if (!xdr_bool(xdrs, &objp->open_none_delegation4_u. + ond_server_will_signal_avail)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_open_delegation4(XDR *xdrs, open_delegation4 *objp) +{ + if (!xdr_open_delegation_type4(xdrs, &objp->delegation_type)) + return (FALSE); + switch (objp->delegation_type) { + case OPEN_DELEGATE_NONE: + break; + case OPEN_DELEGATE_READ: + if (!xdr_open_read_delegation4(xdrs, + &objp->open_delegation4_u.read)) + return (FALSE); + break; + case OPEN_DELEGATE_WRITE: + if (!xdr_open_write_delegation4(xdrs, + &objp->open_delegation4_u.write)) + return (FALSE); + break; + case OPEN_DELEGATE_NONE_EXT: + if (!xdr_open_none_delegation4(xdrs, + &objp->open_delegation4_u.od_whynone)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_gsshandle4_t(XDR *xdrs, gsshandle4_t *objp) +{ + if (!xdr_bytes(xdrs, (char **)&objp->gsshandle4_t_val, + (uint_t *)&objp->gsshandle4_t_len, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_gss_cb_handles4(XDR *xdrs, gss_cb_handles4 *objp) +{ + if (!xdr_rpc_gss_svc_t(xdrs, &objp->gcbp_service)) + return (FALSE); + if (!xdr_gsshandle4_t(xdrs, &objp->gcbp_handle_from_server)) + return (FALSE); + if (!xdr_gsshandle4_t(xdrs, &objp->gcbp_handle_from_client)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_callback_sec_parms4(XDR *xdrs, callback_sec_parms4 *objp) +{ + if (!xdr_uint32_t(xdrs, &objp->cb_secflavor)) + return (FALSE); + switch (objp->cb_secflavor) { + case AUTH_NONE: + break; + case AUTH_SYS: + if (!xdr_authsys_parms(xdrs, + &objp->callback_sec_parms4_u.cbsp_sys_cred)) + return (FALSE); + break; + case RPCSEC_GSS: + if (!xdr_gss_cb_handles4(xdrs, + &objp->callback_sec_parms4_u.cbsp_gss_handles)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_BACKCHANNEL_CTL4args(XDR *xdrs, BACKCHANNEL_CTL4args *objp) +{ + if (!xdr_uint32_t(xdrs, &objp->bca_cb_program)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->bca_sec_parms.bca_sec_parms_val, + (uint_t *)&objp->bca_sec_parms.bca_sec_parms_len, ~0, + sizeof (callback_sec_parms4), (xdrproc_t)xdr_callback_sec_parms4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_BACKCHANNEL_CTL4res(XDR *xdrs, BACKCHANNEL_CTL4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->bcr_status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_channel_dir_from_client4(XDR *xdrs, channel_dir_from_client4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_BIND_CONN_TO_SESSION4args(XDR *xdrs, BIND_CONN_TO_SESSION4args *objp) +{ + if (!xdr_sessionid4(xdrs, objp->bctsa_sessid)) + return (FALSE); + if (!xdr_channel_dir_from_client4(xdrs, &objp->bctsa_dir)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->bctsa_use_conn_in_rdma_mode)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_channel_dir_from_server4(XDR *xdrs, channel_dir_from_server4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_BIND_CONN_TO_SESSION4resok(XDR *xdrs, BIND_CONN_TO_SESSION4resok *objp) +{ + if (!xdr_sessionid4(xdrs, objp->bctsr_sessid)) + return (FALSE); + if (!xdr_channel_dir_from_server4(xdrs, &objp->bctsr_dir)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->bctsr_use_conn_in_rdma_mode)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_BIND_CONN_TO_SESSION4res(XDR *xdrs, BIND_CONN_TO_SESSION4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->bctsr_status)) + return (FALSE); + switch (objp->bctsr_status) { + case NFS4_OK: + if (!xdr_BIND_CONN_TO_SESSION4resok(xdrs, + &objp->BIND_CONN_TO_SESSION4res_u.bctsr_resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_state_protect_ops4(XDR *xdrs, state_protect_ops4 *objp) +{ + if (!xdr_bitmap4(xdrs, &objp->spo_must_enforce)) + return (FALSE); + if (!xdr_bitmap4(xdrs, &objp->spo_must_allow)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_ssv_sp_parms4(XDR *xdrs, ssv_sp_parms4 *objp) +{ + if (!xdr_state_protect_ops4(xdrs, &objp->ssp_ops)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->ssp_hash_algs.ssp_hash_algs_val, + (uint_t *)&objp->ssp_hash_algs.ssp_hash_algs_len, ~0, + sizeof (sec_oid4), (xdrproc_t)xdr_sec_oid4)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->ssp_encr_algs.ssp_encr_algs_val, + (uint_t *)&objp->ssp_encr_algs.ssp_encr_algs_len, ~0, + sizeof (sec_oid4), (xdrproc_t)xdr_sec_oid4)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->ssp_window)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->ssp_num_gss_handles)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_state_protect_how4(XDR *xdrs, state_protect_how4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_state_protect4_a(XDR *xdrs, state_protect4_a *objp) +{ + if (!xdr_state_protect_how4(xdrs, &objp->spa_how)) + return (FALSE); + switch (objp->spa_how) { + case SP4_NONE: + break; + case SP4_MACH_CRED: + if (!xdr_state_protect_ops4(xdrs, + &objp->state_protect4_a_u.spa_mach_ops)) + return (FALSE); + break; + case SP4_SSV: + if (!xdr_ssv_sp_parms4(xdrs, + &objp->state_protect4_a_u.spa_ssv_parms)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_EXCHANGE_ID4args(XDR *xdrs, EXCHANGE_ID4args *objp) +{ + if (!xdr_client_owner4(xdrs, &objp->eia_clientowner)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->eia_flags)) + return (FALSE); + if (!xdr_state_protect4_a(xdrs, &objp->eia_state_protect)) + return (FALSE); + if (!xdr_array(xdrs, + (char **)&objp->eia_client_impl_id.eia_client_impl_id_val, + (uint_t *)&objp->eia_client_impl_id.eia_client_impl_id_len, 1, + sizeof (nfs_impl_id4), (xdrproc_t)xdr_nfs_impl_id4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_ssv_prot_info4(XDR *xdrs, ssv_prot_info4 *objp) +{ + if (!xdr_state_protect_ops4(xdrs, &objp->spi_ops)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->spi_hash_alg)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->spi_encr_alg)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->spi_ssv_len)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->spi_window)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->spi_handles.spi_handles_val, + (uint_t *)&objp->spi_handles.spi_handles_len, ~0, + sizeof (gsshandle4_t), (xdrproc_t)xdr_gsshandle4_t)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_state_protect4_r(XDR *xdrs, state_protect4_r *objp) +{ + if (!xdr_state_protect_how4(xdrs, &objp->spr_how)) + return (FALSE); + switch (objp->spr_how) { + case SP4_NONE: + break; + case SP4_MACH_CRED: + if (!xdr_state_protect_ops4(xdrs, + &objp->state_protect4_r_u.spr_mach_ops)) + return (FALSE); + break; + case SP4_SSV: + if (!xdr_ssv_prot_info4(xdrs, + &objp->state_protect4_r_u.spr_ssv_info)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_EXCHANGE_ID4resok(XDR *xdrs, EXCHANGE_ID4resok *objp) +{ + if (!xdr_clientid4(xdrs, &objp->eir_clientid)) + return (FALSE); + if (!xdr_sequenceid4(xdrs, &objp->eir_sequenceid)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->eir_flags)) + return (FALSE); + if (!xdr_state_protect4_r(xdrs, &objp->eir_state_protect)) + return (FALSE); + if (!xdr_server_owner4(xdrs, &objp->eir_server_owner)) + return (FALSE); + if (!xdr_bytes(xdrs, + (char **)&objp->eir_server_scope.eir_server_scope_val, + (uint_t *)&objp->eir_server_scope.eir_server_scope_len, + NFS4_OPAQUE_LIMIT)) + return (FALSE); + if (!xdr_array(xdrs, + (char **)&objp->eir_server_impl_id.eir_server_impl_id_val, + (uint_t *)&objp->eir_server_impl_id.eir_server_impl_id_len, 1, + sizeof (nfs_impl_id4), (xdrproc_t)xdr_nfs_impl_id4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_EXCHANGE_ID4res(XDR *xdrs, EXCHANGE_ID4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->eir_status)) + return (FALSE); + switch (objp->eir_status) { + case NFS4_OK: + if (!xdr_EXCHANGE_ID4resok(xdrs, + &objp->EXCHANGE_ID4res_u.eir_resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_channel_attrs4(XDR *xdrs, channel_attrs4 *objp) +{ + if (!xdr_count4(xdrs, &objp->ca_headerpadsize)) + return (FALSE); + if (!xdr_count4(xdrs, &objp->ca_maxrequestsize)) + return (FALSE); + if (!xdr_count4(xdrs, &objp->ca_maxresponsesize)) + return (FALSE); + if (!xdr_count4(xdrs, &objp->ca_maxresponsesize_cached)) + return (FALSE); + if (!xdr_count4(xdrs, &objp->ca_maxoperations)) + return (FALSE); + if (!xdr_count4(xdrs, &objp->ca_maxrequests)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->ca_rdma_ird.ca_rdma_ird_val, + (uint_t *)&objp->ca_rdma_ird.ca_rdma_ird_len, 1, + sizeof (uint32_t), (xdrproc_t)xdr_uint32_t)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CREATE_SESSION4args(XDR *xdrs, CREATE_SESSION4args *objp) +{ + + if (!xdr_clientid4(xdrs, &objp->csa_clientid)) + return (FALSE); + if (!xdr_sequenceid4(xdrs, &objp->csa_sequence)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->csa_flags)) + return (FALSE); + if (!xdr_channel_attrs4(xdrs, &objp->csa_fore_chan_attrs)) + return (FALSE); + if (!xdr_channel_attrs4(xdrs, &objp->csa_back_chan_attrs)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->csa_cb_program)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->csa_sec_parms.csa_sec_parms_val, + (uint_t *)&objp->csa_sec_parms.csa_sec_parms_len, ~0, + sizeof (callback_sec_parms4), (xdrproc_t)xdr_callback_sec_parms4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CREATE_SESSION4resok(XDR *xdrs, CREATE_SESSION4resok *objp) +{ + if (!xdr_sessionid4(xdrs, objp->csr_sessionid)) + return (FALSE); + if (!xdr_sequenceid4(xdrs, &objp->csr_sequence)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->csr_flags)) + return (FALSE); + if (!xdr_channel_attrs4(xdrs, &objp->csr_fore_chan_attrs)) + return (FALSE); + if (!xdr_channel_attrs4(xdrs, &objp->csr_back_chan_attrs)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CREATE_SESSION4res(XDR *xdrs, CREATE_SESSION4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->csr_status)) + return (FALSE); + switch (objp->csr_status) { + case NFS4_OK: + if (!xdr_CREATE_SESSION4resok(xdrs, + &objp->CREATE_SESSION4res_u.csr_resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_DESTROY_SESSION4args(XDR *xdrs, DESTROY_SESSION4args *objp) +{ + if (!xdr_sessionid4(xdrs, objp->dsa_sessionid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_DESTROY_SESSION4res(XDR *xdrs, DESTROY_SESSION4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->dsr_status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_FREE_STATEID4args(XDR *xdrs, FREE_STATEID4args *objp) +{ + if (!xdr_stateid4(xdrs, &objp->fsa_stateid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_FREE_STATEID4res(XDR *xdrs, FREE_STATEID4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->fsr_status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_attr_notice4(XDR *xdrs, attr_notice4 *objp) +{ + if (!xdr_nfstime4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_GET_DIR_DELEGATION4args(XDR *xdrs, GET_DIR_DELEGATION4args *objp) +{ + if (!xdr_bool(xdrs, &objp->gdda_signal_deleg_avail)) + return (FALSE); + if (!xdr_bitmap4(xdrs, &objp->gdda_notification_types)) + return (FALSE); + if (!xdr_attr_notice4(xdrs, &objp->gdda_child_attr_delay)) + return (FALSE); + if (!xdr_attr_notice4(xdrs, &objp->gdda_dir_attr_delay)) + return (FALSE); + if (!xdr_bitmap4(xdrs, &objp->gdda_child_attributes)) + return (FALSE); + if (!xdr_bitmap4(xdrs, &objp->gdda_dir_attributes)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_GET_DIR_DELEGATION4resok(XDR *xdrs, GET_DIR_DELEGATION4resok *objp) +{ + if (!xdr_verifier4(xdrs, (verifier4 *)&objp->gddr_cookieverf)) + return (FALSE); + if (!xdr_stateid4(xdrs, &objp->gddr_stateid)) + return (FALSE); + if (!xdr_bitmap4(xdrs, &objp->gddr_notification)) + return (FALSE); + if (!xdr_bitmap4(xdrs, &objp->gddr_child_attributes)) + return (FALSE); + if (!xdr_bitmap4(xdrs, &objp->gddr_dir_attributes)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_gddrnf4_status(XDR *xdrs, gddrnf4_status *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_GET_DIR_DELEGATION4res_non_fatal(XDR *xdrs, + GET_DIR_DELEGATION4res_non_fatal *objp) +{ + if (!xdr_gddrnf4_status(xdrs, &objp->gddrnf_status)) + return (FALSE); + switch (objp->gddrnf_status) { + case GDD4_OK: + if (!xdr_GET_DIR_DELEGATION4resok(xdrs, + &objp->GET_DIR_DELEGATION4res_non_fatal_u.gddrnf_resok4)) + return (FALSE); + break; + case GDD4_UNAVAIL: + if (!xdr_bool(xdrs, &objp->GET_DIR_DELEGATION4res_non_fatal_u. + gddrnf_will_signal_deleg_avail)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_GET_DIR_DELEGATION4res(XDR *xdrs, GET_DIR_DELEGATION4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->gddr_status)) + return (FALSE); + switch (objp->gddr_status) { + case NFS4_OK: + if (!xdr_GET_DIR_DELEGATION4res_non_fatal(xdrs, + &objp->GET_DIR_DELEGATION4res_u.gddr_res_non_fatal4)) + return (FALSE); + break; + } + return (TRUE); +} + +/* + * Special xdr function to encode single word bitmaps for + * notification bitmaps which only need a single word. + */ +bool_t +xdr_bitmap4_notify(XDR *xdrs, bitmap4 *objp) +{ + int32_t len = 1; + + ASSERT(xdrs->x_op == XDR_ENCODE); + if (!XDR_PUTINT32(xdrs, &len)) + return (FALSE); +#if defined(_BIG_ENDIAN) + return (XDR_PUTINT32(xdrs, (int32_t *)objp)); +#elif defined(_LITTLE_ENDIAN) + return (XDR_PUTINT32(xdrs, (int32_t *)objp+1)); +#endif +} + +bool_t +xdr_GETDEVICEINFO4args(XDR *xdrs, GETDEVICEINFO4args *objp) +{ + if (!xdr_deviceid4(xdrs, objp->gdia_device_id)) + return (FALSE); + if (!xdr_layouttype4(xdrs, &objp->gdia_layout_type)) + return (FALSE); + if (!xdr_count4(xdrs, &objp->gdia_maxcount)) + return (FALSE); + if (xdrs->x_op == XDR_ENCODE) { + if (!xdr_bitmap4_notify(xdrs, &objp->gdia_notify_types)) + return (FALSE); + } else + if (!xdr_bitmap4(xdrs, &objp->gdia_notify_types)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_GETDEVICEINFO4resok(XDR *xdrs, GETDEVICEINFO4resok *objp) +{ + if (!xdr_device_addr4(xdrs, &objp->gdir_device_addr)) + return (FALSE); + if (xdrs->x_op == XDR_ENCODE) { + if (!xdr_bitmap4_notify(xdrs, &objp->gdir_notification)) + return (FALSE); + } else + if (!xdr_bitmap4(xdrs, &objp->gdir_notification)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_GETDEVICEINFO4res(XDR *xdrs, GETDEVICEINFO4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->gdir_status)) + return (FALSE); + switch (objp->gdir_status) { + case NFS4_OK: + if (!xdr_GETDEVICEINFO4resok(xdrs, + &objp->GETDEVICEINFO4res_u.gdir_resok4)) + return (FALSE); + break; + case NFS4ERR_TOOSMALL: + if (!xdr_count4(xdrs, &objp->GETDEVICEINFO4res_u.gdir_mincount)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_GETDEVICELIST4args(XDR *xdrs, GETDEVICELIST4args *objp) +{ + if (!xdr_layouttype4(xdrs, &objp->gdla_layout_type)) + return (FALSE); + if (!xdr_count4(xdrs, &objp->gdla_maxdevices)) + return (FALSE); + if (!xdr_nfs_cookie4(xdrs, &objp->gdla_cookie)) + return (FALSE); + if (!xdr_verifier4(xdrs, (verifier4 *)&objp->gdla_cookieverf)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_GETDEVICELIST4resok(XDR *xdrs, GETDEVICELIST4resok *objp) +{ + if (!xdr_nfs_cookie4(xdrs, &objp->gdlr_cookie)) + return (FALSE); + if (!xdr_verifier4(xdrs, (verifier4 *)&objp->gdlr_cookieverf)) + return (FALSE); + if (!xdr_array(xdrs, + (char **)&objp->gdlr_deviceid_list.gdlr_deviceid_list_val, + (uint_t *)&objp->gdlr_deviceid_list.gdlr_deviceid_list_len, + ~0, sizeof (deviceid4), (xdrproc_t)xdr_deviceid4)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->gdlr_eof)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_GETDEVICELIST4res(XDR *xdrs, GETDEVICELIST4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->gdlr_status)) + return (FALSE); + switch (objp->gdlr_status) { + case NFS4_OK: + if (!xdr_GETDEVICELIST4resok(xdrs, + &objp->GETDEVICELIST4res_u.gdlr_resok4)) + return (FALSE); + break; + default: + break; + } + return (TRUE); +} + +bool_t +xdr_newtime4(XDR *xdrs, newtime4 *objp) +{ + if (!xdr_bool(xdrs, &objp->nt_timechanged)) + return (FALSE); + switch (objp->nt_timechanged) { + case TRUE: + if (!xdr_nfstime4(xdrs, &objp->newtime4_u.nt_time)) + return (FALSE); + break; + case FALSE: + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_newoffset4(XDR *xdrs, newoffset4 *objp) +{ + if (!xdr_bool(xdrs, &objp->no_newoffset)) + return (FALSE); + switch (objp->no_newoffset) { + case TRUE: + if (!xdr_offset4(xdrs, &objp->newoffset4_u.no_offset)) + return (FALSE); + break; + case FALSE: + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_LAYOUTCOMMIT4args(XDR *xdrs, LAYOUTCOMMIT4args *objp) +{ + if (!xdr_offset4(xdrs, &objp->loca_offset)) + return (FALSE); + if (!xdr_length4(xdrs, &objp->loca_length)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->loca_reclaim)) + return (FALSE); + if (!xdr_stateid4(xdrs, &objp->loca_stateid)) + return (FALSE); + if (!xdr_newoffset4(xdrs, &objp->loca_last_write_offset)) + return (FALSE); + if (!xdr_newtime4(xdrs, &objp->loca_time_modify)) + return (FALSE); + if (!xdr_layoutupdate4(xdrs, &objp->loca_layoutupdate)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_newsize4(XDR *xdrs, newsize4 *objp) +{ + if (!xdr_bool(xdrs, &objp->ns_sizechanged)) + return (FALSE); + switch (objp->ns_sizechanged) { + case TRUE: + if (!xdr_length4(xdrs, &objp->newsize4_u.ns_size)) + return (FALSE); + break; + case FALSE: + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_LAYOUTCOMMIT4resok(XDR *xdrs, LAYOUTCOMMIT4resok *objp) +{ + if (!xdr_newsize4(xdrs, &objp->locr_newsize)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_LAYOUTCOMMIT4res(XDR *xdrs, LAYOUTCOMMIT4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->locr_status)) + return (FALSE); + switch (objp->locr_status) { + case NFS4_OK: + if (!xdr_LAYOUTCOMMIT4resok(xdrs, + &objp->LAYOUTCOMMIT4res_u.locr_resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_LAYOUTGET4args(XDR *xdrs, LAYOUTGET4args *objp) +{ + if (!xdr_bool(xdrs, &objp->loga_signal_layout_avail)) + return (FALSE); + if (!xdr_layouttype4(xdrs, &objp->loga_layout_type)) + return (FALSE); + if (!xdr_layoutiomode4(xdrs, &objp->loga_iomode)) + return (FALSE); + if (!xdr_offset4(xdrs, &objp->loga_offset)) + return (FALSE); + if (!xdr_length4(xdrs, &objp->loga_length)) + return (FALSE); + if (!xdr_length4(xdrs, &objp->loga_minlength)) + return (FALSE); + if (!xdr_stateid4(xdrs, &objp->loga_stateid)) + return (FALSE); + if (!xdr_count4(xdrs, &objp->loga_maxcount)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_LAYOUTGET4resok(XDR *xdrs, LAYOUTGET4resok *objp) +{ + if (!xdr_bool(xdrs, &objp->logr_return_on_close)) + return (FALSE); + if (!xdr_stateid4(xdrs, &objp->logr_stateid)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->logr_layout.logr_layout_val, + (uint_t *)&objp->logr_layout.logr_layout_len, ~0, + sizeof (layout4), (xdrproc_t)xdr_layout4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_LAYOUTGET4res(XDR *xdrs, LAYOUTGET4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->logr_status)) + return (FALSE); + switch (objp->logr_status) { + case NFS4_OK: + if (!xdr_LAYOUTGET4resok(xdrs, + &objp->LAYOUTGET4res_u.logr_resok4)) + return (FALSE); + break; + case NFS4ERR_LAYOUTTRYLATER: + if (!xdr_bool(xdrs, + &objp->LAYOUTGET4res_u.logr_will_signal_layout_avail)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_LAYOUTRETURN4args(XDR *xdrs, LAYOUTRETURN4args *objp) +{ + if (!xdr_bool(xdrs, &objp->lora_reclaim)) + return (FALSE); + if (!xdr_layouttype4(xdrs, &objp->lora_layout_type)) + return (FALSE); + if (!xdr_layoutiomode4(xdrs, &objp->lora_iomode)) + return (FALSE); + if (!xdr_layoutreturn4(xdrs, &objp->lora_layoutreturn)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_layoutreturn_stateid(XDR *xdrs, layoutreturn_stateid *objp) +{ + if (!xdr_bool(xdrs, &objp->lrs_present)) + return (FALSE); + switch (objp->lrs_present) { + case TRUE: + if (!xdr_stateid4(xdrs, + &objp->layoutreturn_stateid_u.lrs_stateid)) + return (FALSE); + break; + case FALSE: + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_LAYOUTRETURN4res(XDR *xdrs, LAYOUTRETURN4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->lorr_status)) + return (FALSE); + switch (objp->lorr_status) { + case NFS4_OK: + if (!xdr_layoutreturn_stateid(xdrs, + &objp->LAYOUTRETURN4res_u.lorr_stateid)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_secinfo_style4(XDR *xdrs, secinfo_style4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SECINFO_NO_NAME4args(XDR *xdrs, SECINFO_NO_NAME4args *objp) +{ + if (!xdr_secinfo_style4(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SECINFO_NO_NAME4res(XDR *xdrs, SECINFO_NO_NAME4res *objp) +{ + if (!xdr_SECINFO4res(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SEQUENCE4args(XDR *xdrs, SEQUENCE4args *objp) +{ + if (!xdr_sessionid4(xdrs, objp->sa_sessionid)) + return (FALSE); + if (!xdr_sequenceid4(xdrs, &objp->sa_sequenceid)) + return (FALSE); + if (!xdr_slotid4(xdrs, &objp->sa_slotid)) + return (FALSE); + if (!xdr_slotid4(xdrs, &objp->sa_highest_slotid)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->sa_cachethis)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SEQUENCE4resok(XDR *xdrs, SEQUENCE4resok *objp) +{ + if (!xdr_sessionid4(xdrs, objp->sr_sessionid)) + return (FALSE); + if (!xdr_sequenceid4(xdrs, &objp->sr_sequenceid)) + return (FALSE); + if (!xdr_slotid4(xdrs, &objp->sr_slotid)) + return (FALSE); + if (!xdr_slotid4(xdrs, &objp->sr_highest_slotid)) + return (FALSE); + if (!xdr_slotid4(xdrs, &objp->sr_target_highest_slotid)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->sr_status_flags)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SEQUENCE4res(XDR *xdrs, SEQUENCE4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->sr_status)) + return (FALSE); + switch (objp->sr_status) { + case NFS4_OK: + if (!xdr_SEQUENCE4resok(xdrs, &objp->SEQUENCE4res_u.sr_resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_ssa_digest_input4(XDR *xdrs, ssa_digest_input4 *objp) +{ + if (!xdr_SEQUENCE4args(xdrs, &objp->sdi_seqargs)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SET_SSV4args(XDR *xdrs, SET_SSV4args *objp) +{ + if (!xdr_bytes(xdrs, (char **)&objp->ssa_ssv.ssa_ssv_val, + (uint_t *)&objp->ssa_ssv.ssa_ssv_len, ~0)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->ssa_digest.ssa_digest_val, + (uint_t *)&objp->ssa_digest.ssa_digest_len, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_ssr_digest_input4(XDR *xdrs, ssr_digest_input4 *objp) +{ + if (!xdr_SEQUENCE4res(xdrs, &objp->sdi_seqres)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SET_SSV4resok(XDR *xdrs, SET_SSV4resok *objp) +{ + if (!xdr_bytes(xdrs, (char **)&objp->ssr_digest.ssr_digest_val, + (uint_t *)&objp->ssr_digest.ssr_digest_len, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_SET_SSV4res(XDR *xdrs, SET_SSV4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->ssr_status)) + return (FALSE); + switch (objp->ssr_status) { + case NFS4_OK: + if (!xdr_SET_SSV4resok(xdrs, &objp->SET_SSV4res_u.ssr_resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_TEST_STATEID4args(XDR *xdrs, TEST_STATEID4args *objp) +{ + if (!xdr_array(xdrs, (char **)&objp->ts_stateids.ts_stateids_val, + (uint_t *)&objp->ts_stateids.ts_stateids_len, ~0, + sizeof (stateid4), (xdrproc_t)xdr_stateid4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_TEST_STATEID4resok(XDR *xdrs, TEST_STATEID4resok *objp) +{ + if (!xdr_array(xdrs, + (char **)&objp->tsr_status_codes.tsr_status_codes_val, + (uint_t *)&objp->tsr_status_codes.tsr_status_codes_len, ~0, + sizeof (nfsstat4), (xdrproc_t)xdr_nfsstat4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_TEST_STATEID4res(XDR *xdrs, TEST_STATEID4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->tsr_status)) + return (FALSE); + switch (objp->tsr_status) { + case NFS4_OK: + if (!xdr_TEST_STATEID4resok(xdrs, + &objp->TEST_STATEID4res_u.tsr_resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_deleg_claim4(XDR *xdrs, deleg_claim4 *objp) +{ + if (!xdr_open_claim_type4(xdrs, &objp->dc_claim)) + return (FALSE); + switch (objp->dc_claim) { + case CLAIM_FH: + break; + case CLAIM_DELEG_PREV_FH: + break; + case CLAIM_PREVIOUS: + if (!xdr_open_delegation_type4(xdrs, + &objp->deleg_claim4_u.dc_delegate_type)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_WANT_DELEGATION4args(XDR *xdrs, WANT_DELEGATION4args *objp) +{ + if (!xdr_uint32_t(xdrs, &objp->wda_want)) + return (FALSE); + if (!xdr_deleg_claim4(xdrs, &objp->wda_claim)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_WANT_DELEGATION4res(XDR *xdrs, WANT_DELEGATION4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->wdr_status)) + return (FALSE); + switch (objp->wdr_status) { + case NFS4_OK: + if (!xdr_open_delegation4(xdrs, + &objp->WANT_DELEGATION4res_u.wdr_resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_DESTROY_CLIENTID4args(XDR *xdrs, DESTROY_CLIENTID4args *objp) +{ + if (!xdr_clientid4(xdrs, &objp->dca_clientid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_DESTROY_CLIENTID4res(XDR *xdrs, DESTROY_CLIENTID4res *objp) +{ + + if (!xdr_nfsstat4(xdrs, &objp->dcr_status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_RECLAIM_COMPLETE4args(XDR *xdrs, RECLAIM_COMPLETE4args *objp) +{ + if (!xdr_bool(xdrs, &objp->rca_one_fs)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_RECLAIM_COMPLETE4res(XDR *xdrs, RECLAIM_COMPLETE4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->rcr_status)) + return (FALSE); + return (TRUE); +} + +/* new operations for NFSv4.1 */ + +bool_t +xdr_nfs4x_argop4(XDR *xdrs, nfs_argop4 *objp) +{ + /* nfs_opnum4 has already been xdr'd */ + switch (objp->argop) { + case OP_BACKCHANNEL_CTL: + if (!xdr_BACKCHANNEL_CTL4args(xdrs, + &objp->nfs_argop4_u.opbackchannel_ctl)) + return (FALSE); + break; + case OP_BIND_CONN_TO_SESSION: + if (!xdr_BIND_CONN_TO_SESSION4args(xdrs, + &objp->nfs_argop4_u.opbind_conn_to_session)) + return (FALSE); + break; + case OP_EXCHANGE_ID: + if (!xdr_EXCHANGE_ID4args(xdrs, + &objp->nfs_argop4_u.opexchange_id)) + return (FALSE); + break; + case OP_CREATE_SESSION: + if (!xdr_CREATE_SESSION4args(xdrs, + &objp->nfs_argop4_u.opcreate_session)) + return (FALSE); + break; + case OP_DESTROY_SESSION: + if (!xdr_DESTROY_SESSION4args(xdrs, + &objp->nfs_argop4_u.opdestroy_session)) + return (FALSE); + break; + case OP_FREE_STATEID: + if (!xdr_FREE_STATEID4args(xdrs, + &objp->nfs_argop4_u.opfree_stateid)) + return (FALSE); + break; + case OP_GET_DIR_DELEGATION: + if (!xdr_GET_DIR_DELEGATION4args(xdrs, + &objp->nfs_argop4_u.opget_dir_delegation)) + return (FALSE); + break; + case OP_GETDEVICEINFO: + if (!xdr_GETDEVICEINFO4args(xdrs, + &objp->nfs_argop4_u.opgetdeviceinfo)) + return (FALSE); + break; + case OP_GETDEVICELIST: + if (!xdr_GETDEVICELIST4args(xdrs, + &objp->nfs_argop4_u.opgetdevicelist)) + return (FALSE); + break; + case OP_LAYOUTCOMMIT: + if (!xdr_LAYOUTCOMMIT4args(xdrs, + &objp->nfs_argop4_u.oplayoutcommit)) + return (FALSE); + break; + case OP_LAYOUTGET: + if (!xdr_LAYOUTGET4args(xdrs, + &objp->nfs_argop4_u.oplayoutget)) + return (FALSE); + break; + case OP_LAYOUTRETURN: + if (!xdr_LAYOUTRETURN4args(xdrs, + &objp->nfs_argop4_u.oplayoutreturn)) + return (FALSE); + break; + case OP_SECINFO_NO_NAME: + if (!xdr_SECINFO_NO_NAME4args(xdrs, + &objp->nfs_argop4_u.opsecinfo_no_name)) + return (FALSE); + break; + case OP_SEQUENCE: + if (!xdr_SEQUENCE4args(xdrs, + &objp->nfs_argop4_u.opsequence)) + return (FALSE); + break; + case OP_SET_SSV: + if (!xdr_SET_SSV4args(xdrs, + &objp->nfs_argop4_u.opset_ssv)) + return (FALSE); + break; + case OP_TEST_STATEID: + if (!xdr_TEST_STATEID4args(xdrs, + &objp->nfs_argop4_u.optest_stateid)) + return (FALSE); + break; + case OP_WANT_DELEGATION: + if (!xdr_WANT_DELEGATION4args(xdrs, + &objp->nfs_argop4_u.opwant_delegation)) + return (FALSE); + break; + case OP_DESTROY_CLIENTID: + if (!xdr_DESTROY_CLIENTID4args(xdrs, + &objp->nfs_argop4_u.opdestroy_clientid)) + return (FALSE); + break; + case OP_RECLAIM_COMPLETE: + if (!xdr_RECLAIM_COMPLETE4args(xdrs, + &objp->nfs_argop4_u.opreclaim_complete)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_nfs4x_resop4(XDR *xdrs, nfs_resop4 *objp) +{ + /* nfs_opnum4 has already been xdr's */ + switch (objp->resop) { + case OP_BACKCHANNEL_CTL: + if (!xdr_BACKCHANNEL_CTL4res(xdrs, + &objp->nfs_resop4_u.opbackchannel_ctl)) + return (FALSE); + break; + case OP_BIND_CONN_TO_SESSION: + if (!xdr_BIND_CONN_TO_SESSION4res(xdrs, + &objp->nfs_resop4_u.opbind_conn_to_session)) + return (FALSE); + break; + case OP_EXCHANGE_ID: + if (!xdr_EXCHANGE_ID4res(xdrs, + &objp->nfs_resop4_u.opexchange_id)) + return (FALSE); + break; + case OP_CREATE_SESSION: + if (!xdr_CREATE_SESSION4res(xdrs, + &objp->nfs_resop4_u.opcreate_session)) + return (FALSE); + break; + case OP_DESTROY_SESSION: + if (!xdr_DESTROY_SESSION4res(xdrs, + &objp->nfs_resop4_u.opdestroy_session)) + return (FALSE); + break; + case OP_FREE_STATEID: + if (!xdr_FREE_STATEID4res(xdrs, + &objp->nfs_resop4_u.opfree_stateid)) + return (FALSE); + break; + case OP_GET_DIR_DELEGATION: + if (!xdr_GET_DIR_DELEGATION4res(xdrs, + &objp->nfs_resop4_u.opget_dir_delegation)) + return (FALSE); + break; + case OP_GETDEVICEINFO: + if (!xdr_GETDEVICEINFO4res(xdrs, + &objp->nfs_resop4_u.opgetdeviceinfo)) + return (FALSE); + break; + case OP_GETDEVICELIST: + if (!xdr_GETDEVICELIST4res(xdrs, + &objp->nfs_resop4_u.opgetdevicelist)) + return (FALSE); + break; + case OP_LAYOUTCOMMIT: + if (!xdr_LAYOUTCOMMIT4res(xdrs, + &objp->nfs_resop4_u.oplayoutcommit)) + return (FALSE); + break; + case OP_LAYOUTGET: + if (!xdr_LAYOUTGET4res(xdrs, + &objp->nfs_resop4_u.oplayoutget)) + return (FALSE); + break; + case OP_LAYOUTRETURN: + if (!xdr_LAYOUTRETURN4res(xdrs, + &objp->nfs_resop4_u.oplayoutreturn)) + return (FALSE); + break; + case OP_SECINFO_NO_NAME: + if (!xdr_SECINFO_NO_NAME4res(xdrs, + &objp->nfs_resop4_u.opsecinfo_no_name)) + return (FALSE); + break; + case OP_SEQUENCE: + if (!xdr_SEQUENCE4res(xdrs, + &objp->nfs_resop4_u.opsequence)) + return (FALSE); + break; + case OP_SET_SSV: + if (!xdr_SET_SSV4res(xdrs, + &objp->nfs_resop4_u.opset_ssv)) + return (FALSE); + break; + case OP_TEST_STATEID: + if (!xdr_TEST_STATEID4res(xdrs, + &objp->nfs_resop4_u.optest_stateid)) + return (FALSE); + break; + case OP_WANT_DELEGATION: + if (!xdr_WANT_DELEGATION4res(xdrs, + &objp->nfs_resop4_u.opwant_delegation)) + return (FALSE); + break; + case OP_DESTROY_CLIENTID: + if (!xdr_DESTROY_CLIENTID4res(xdrs, + &objp->nfs_resop4_u.opdestroy_clientid)) + return (FALSE); + break; + case OP_RECLAIM_COMPLETE: + if (!xdr_RECLAIM_COMPLETE4res(xdrs, + &objp->nfs_resop4_u.opreclaim_complete)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_layoutrecall_type4(XDR *xdrs, layoutrecall_type4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_layoutrecall_file4(XDR *xdrs, layoutrecall_file4 *objp) +{ + switch (xdrs->x_op) { + case XDR_ENCODE: + /* TODO: encode nfs4x_fh */ + return (FALSE); + + case XDR_DECODE: + if (!xdr_bytes(xdrs, (char **)&objp->lor_fh.nfs_fh4_val, + (uint_t *)&objp->lor_fh.nfs_fh4_len, NFS4_FHSIZE)) + return (FALSE); + break; + + case XDR_FREE: + if (objp->lor_fh.nfs_fh4_val != NULL) { + if (!xdr_bytes(xdrs, + (char **)&objp->lor_fh.nfs_fh4_val, + (uint_t *)&objp->lor_fh.nfs_fh4_len, + NFS4_FHSIZE)) + return (FALSE); + } + break; + } + + if (!xdr_offset4(xdrs, &objp->lor_offset)) + return (FALSE); + if (!xdr_length4(xdrs, &objp->lor_length)) + return (FALSE); + if (!xdr_stateid4(xdrs, &objp->lor_stateid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_layoutrecall4(XDR *xdrs, layoutrecall4 *objp) +{ + if (!xdr_layoutrecall_type4(xdrs, &objp->lor_recalltype)) + return (FALSE); + switch (objp->lor_recalltype) { + case LAYOUTRECALL4_FILE: + if (!xdr_layoutrecall_file4(xdrs, + &objp->layoutrecall4_u.lor_layout)) + return (FALSE); + break; + case LAYOUTRECALL4_FSID: + if (!xdr_fsid4(xdrs, &objp->layoutrecall4_u.lor_fsid)) + return (FALSE); + break; + case LAYOUTRECALL4_ALL: + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_CB_LAYOUTRECALL4args(XDR *xdrs, CB_LAYOUTRECALL4args *objp) +{ + if (!xdr_layouttype4(xdrs, &objp->clora_type)) + return (FALSE); + if (!xdr_layoutiomode4(xdrs, &objp->clora_iomode)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->clora_changed)) + return (FALSE); + if (!xdr_layoutrecall4(xdrs, &objp->clora_recall)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_LAYOUTRECALL4res(XDR *xdrs, CB_LAYOUTRECALL4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->clorr_status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_notify_type4(XDR *xdrs, notify_type4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_notify_entry4(XDR *xdrs, notify_entry4 *objp) +{ + if (!xdr_component4(xdrs, &objp->ne_file)) + return (FALSE); + if (!xdr_fattr4(xdrs, &objp->ne_attrs)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_prev_entry4(XDR *xdrs, prev_entry4 *objp) +{ + if (!xdr_notify_entry4(xdrs, &objp->pe_prev_entry)) + return (FALSE); + if (!xdr_nfs_cookie4(xdrs, &objp->pe_prev_entry_cookie)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_notify_remove4(XDR *xdrs, notify_remove4 *objp) +{ + + if (!xdr_notify_entry4(xdrs, &objp->nrm_old_entry)) + return (FALSE); + if (!xdr_nfs_cookie4(xdrs, &objp->nrm_old_entry_cookie)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_notify_add4(XDR *xdrs, notify_add4 *objp) +{ + if (!xdr_array(xdrs, (char **)&objp->nad_old_entry.nad_old_entry_val, + (uint_t *)&objp->nad_old_entry.nad_old_entry_len, 1, + sizeof (notify_remove4), (xdrproc_t)xdr_notify_remove4)) + return (FALSE); + if (!xdr_notify_entry4(xdrs, &objp->nad_new_entry)) + return (FALSE); + if (!xdr_array(xdrs, + (char **)&objp->nad_new_entry_cookie.nad_new_entry_cookie_val, + (uint_t *)&objp->nad_new_entry_cookie.nad_new_entry_cookie_len, 1, + sizeof (nfs_cookie4), (xdrproc_t)xdr_nfs_cookie4)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->nad_prev_entry.nad_prev_entry_val, + (uint_t *)&objp->nad_prev_entry.nad_prev_entry_len, 1, + sizeof (prev_entry4), (xdrproc_t)xdr_prev_entry4)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->nad_last_entry)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_notify_attr4(XDR *xdrs, notify_attr4 *objp) +{ + if (!xdr_notify_entry4(xdrs, &objp->na_changed_entry)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_notify_rename4(XDR *xdrs, notify_rename4 *objp) +{ + if (!xdr_notify_remove4(xdrs, &objp->nrn_old_entry)) + return (FALSE); + if (!xdr_notify_add4(xdrs, &objp->nrn_new_entry)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_notify_verifier4(XDR *xdrs, notify_verifier4 *objp) +{ + if (!xdr_verifier4(xdrs, (verifier4 *)&objp->nv_old_cookieverf)) + return (FALSE); + if (!xdr_verifier4(xdrs, (verifier4 *)&objp->nv_new_cookieverf)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_notifylist4(XDR *xdrs, notifylist4 *objp) +{ + if (!xdr_bytes(xdrs, (char **)&objp->notifylist4_val, + (uint_t *)&objp->notifylist4_len, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_notify4(XDR *xdrs, notify4 *objp) +{ + if (xdrs->x_op == XDR_ENCODE) { + if (!xdr_bitmap4_notify(xdrs, &objp->notify_mask)) + return (FALSE); + } else + if (!xdr_bitmap4(xdrs, &objp->notify_mask)) + return (FALSE); + if (!xdr_notifylist4(xdrs, &objp->notify_vals)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_NOTIFY4args(XDR *xdrs, CB_NOTIFY4args *objp) +{ + if (!xdr_stateid4(xdrs, &objp->cna_stateid)) + return (FALSE); + if (!xdr_nfs_fh4(xdrs, &objp->cna_fh)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->cna_changes.cna_changes_val, + (uint_t *)&objp->cna_changes.cna_changes_len, ~0, + sizeof (notify4), (xdrproc_t)xdr_notify4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_NOTIFY4res(XDR *xdrs, CB_NOTIFY4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->cnr_status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_PUSH_DELEG4args(XDR *xdrs, CB_PUSH_DELEG4args *objp) +{ + if (!xdr_nfs_fh4(xdrs, &objp->cpda_fh)) + return (FALSE); + if (!xdr_open_delegation4(xdrs, &objp->cpda_delegation)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_PUSH_DELEG4res(XDR *xdrs, CB_PUSH_DELEG4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->cpdr_status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_RECALL_ANY4args(XDR *xdrs, CB_RECALL_ANY4args *objp) +{ + if (!xdr_uint32_t(xdrs, &objp->craa_objects_to_keep)) + return (FALSE); + if (!xdr_bitmap4(xdrs, &objp->craa_type_mask)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_RECALL_ANY4res(XDR *xdrs, CB_RECALL_ANY4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->crar_status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_RECALLABLE_OBJ_AVAIL4args(XDR *xdrs, CB_RECALLABLE_OBJ_AVAIL4args *objp) +{ + if (!xdr_CB_RECALL_ANY4args(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_RECALLABLE_OBJ_AVAIL4res(XDR *xdrs, CB_RECALLABLE_OBJ_AVAIL4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->croa_status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_RECALL_SLOT4args(XDR *xdrs, CB_RECALL_SLOT4args *objp) +{ + if (!xdr_slotid4(xdrs, &objp->rsa_target_highest_slotid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_RECALL_SLOT4res(XDR *xdrs, CB_RECALL_SLOT4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->rsr_status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_referring_call4(XDR *xdrs, referring_call4 *objp) +{ + if (!xdr_sequenceid4(xdrs, &objp->rc_sequenceid)) + return (FALSE); + if (!xdr_slotid4(xdrs, &objp->rc_slotid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_referring_call_list4(XDR *xdrs, referring_call_list4 *objp) +{ + if (!xdr_sessionid4(xdrs, objp->rcl_sessionid)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->rcl_referring_calls. + rcl_referring_calls_val, + (uint_t *)&objp->rcl_referring_calls.rcl_referring_calls_len, ~0, + sizeof (referring_call4), (xdrproc_t)xdr_referring_call4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_SEQUENCE4args(XDR *xdrs, CB_SEQUENCE4args *objp) +{ + if (!xdr_sessionid4(xdrs, objp->csa_sessionid)) + return (FALSE); + if (!xdr_sequenceid4(xdrs, &objp->csa_sequenceid)) + return (FALSE); + if (!xdr_slotid4(xdrs, &objp->csa_slotid)) + return (FALSE); + if (!xdr_slotid4(xdrs, &objp->csa_highest_slotid)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->csa_cachethis)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->csa_referring_call_lists. + csa_referring_call_lists_val, + (uint_t *)&objp->csa_referring_call_lists. + csa_referring_call_lists_len, ~0, sizeof (referring_call_list4), + (xdrproc_t)xdr_referring_call_list4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_SEQUENCE4resok(XDR *xdrs, CB_SEQUENCE4resok *objp) +{ + if (!xdr_sessionid4(xdrs, objp->csr_sessionid)) + return (FALSE); + if (!xdr_sequenceid4(xdrs, &objp->csr_sequenceid)) + return (FALSE); + if (!xdr_slotid4(xdrs, &objp->csr_slotid)) + return (FALSE); + if (!xdr_slotid4(xdrs, &objp->csr_highest_slotid)) + return (FALSE); + if (!xdr_slotid4(xdrs, &objp->csr_target_highest_slotid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_SEQUENCE4res(XDR *xdrs, CB_SEQUENCE4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->csr_status)) + return (FALSE); + switch (objp->csr_status) { + case NFS4_OK: + if (!xdr_CB_SEQUENCE4resok(xdrs, + &objp->CB_SEQUENCE4res_u.csr_resok4)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_CB_WANTS_CANCELLED4args(XDR *xdrs, CB_WANTS_CANCELLED4args *objp) +{ + if (!xdr_bool(xdrs, &objp->cwca_contended_wants_cancelled)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->cwca_resourced_wants_cancelled)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_WANTS_CANCELLED4res(XDR *xdrs, CB_WANTS_CANCELLED4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->cwcr_status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_NOTIFY_LOCK4args(XDR *xdrs, CB_NOTIFY_LOCK4args *objp) +{ + if (!xdr_nfs_fh4(xdrs, &objp->cnla_fh)) + return (FALSE); + if (!xdr_lock_owner4(xdrs, &objp->cnla_lock_owner)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_NOTIFY_LOCK4res(XDR *xdrs, CB_NOTIFY_LOCK4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->cnlr_status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_notify_deviceid_type4(XDR *xdrs, notify_deviceid_type4 *objp) +{ + + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_notify_deviceid_delete4(XDR *xdrs, notify_deviceid_delete4 *objp) +{ + if (!xdr_layouttype4(xdrs, &objp->ndd_layouttype)) + return (FALSE); + if (!xdr_deviceid4(xdrs, objp->ndd_deviceid)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_notify_deviceid_change4(XDR *xdrs, notify_deviceid_change4 *objp) +{ + + if (!xdr_layouttype4(xdrs, &objp->ndc_layouttype)) + return (FALSE); + if (!xdr_deviceid4(xdrs, objp->ndc_deviceid)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->ndc_immediate)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_NOTIFY_DEVICEID4args(XDR *xdrs, CB_NOTIFY_DEVICEID4args *objp) +{ + if (!xdr_array(xdrs, (char **)&objp->cnda_changes.cnda_changes_val, + (uint_t *)&objp->cnda_changes.cnda_changes_len, ~0, + sizeof (notify4), (xdrproc_t)xdr_notify4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_NOTIFY_DEVICEID4res(XDR *xdrs, CB_NOTIFY_DEVICEID4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->cndr_status)) + return (FALSE); + return (TRUE); +} + +/* Callback operations new to NFSv4.1 */ + +bool_t +xdr_nfs_cb_argop4(XDR *xdrs, nfs_cb_argop4 *objp) +{ + /* argop has already been xdr'd */ + switch (objp->argop) { + case OP_CB_LAYOUTRECALL: + if (!xdr_CB_LAYOUTRECALL4args(xdrs, + &objp->nfs_cb_argop4_u.opcblayoutrecall)) + return (FALSE); + break; + case OP_CB_NOTIFY: + if (!xdr_CB_NOTIFY4args(xdrs, + &objp->nfs_cb_argop4_u.opcbnotify)) + return (FALSE); + break; + case OP_CB_PUSH_DELEG: + if (!xdr_CB_PUSH_DELEG4args(xdrs, + &objp->nfs_cb_argop4_u.opcbpush_deleg)) + return (FALSE); + break; + case OP_CB_RECALL_ANY: + if (!xdr_CB_RECALL_ANY4args(xdrs, + &objp->nfs_cb_argop4_u.opcbrecall_any)) + return (FALSE); + break; + case OP_CB_RECALLABLE_OBJ_AVAIL: + if (!xdr_CB_RECALLABLE_OBJ_AVAIL4args(xdrs, + &objp->nfs_cb_argop4_u.opcbrecallable_obj_avail)) + return (FALSE); + break; + case OP_CB_RECALL_SLOT: + if (!xdr_CB_RECALL_SLOT4args(xdrs, + &objp->nfs_cb_argop4_u.opcbrecall_slot)) + return (FALSE); + break; + case OP_CB_SEQUENCE: + if (!xdr_CB_SEQUENCE4args(xdrs, + &objp->nfs_cb_argop4_u.opcbsequence)) + return (FALSE); + break; + case OP_CB_WANTS_CANCELLED: + if (!xdr_CB_WANTS_CANCELLED4args(xdrs, + &objp->nfs_cb_argop4_u.opcbwants_cancelled)) + return (FALSE); + break; + case OP_CB_NOTIFY_LOCK: + if (!xdr_CB_NOTIFY_LOCK4args(xdrs, + &objp->nfs_cb_argop4_u.opcbnotify_lock)) + return (FALSE); + break; + case OP_CB_NOTIFY_DEVICEID: + if (!xdr_CB_NOTIFY_DEVICEID4args(xdrs, + &objp->nfs_cb_argop4_u.opcbnotify_deviceid)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_CB_GETATTR4res(XDR *xdrs, CB_GETATTR4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + + switch (objp->status) { + case NFS4_OK: + if (!xdr_fattr4(xdrs, &objp->obj_attributes)) + return (FALSE); + break; + } + return (TRUE); +} + +bool_t +xdr_CB_RECALL4res(XDR *xdrs, CB_RECALL4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_CB_ILLEGAL4res(XDR *xdrs, CB_ILLEGAL4res *objp) +{ + if (!xdr_nfsstat4(xdrs, &objp->status)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfs_cb_resop4(XDR *xdrs, nfs_cb_resop4 *objp) +{ + if (!xdr_u_int(xdrs, &objp->resop)) + return (FALSE); + + switch (objp->resop) { + case OP_CB_GETATTR: + if (!xdr_CB_GETATTR4res(xdrs, + &objp->nfs_cb_resop4_u.opcbgetattr)) + return (FALSE); + break; + case OP_CB_RECALL: + if (!xdr_CB_RECALL4res(xdrs, + &objp->nfs_cb_resop4_u.opcbrecall)) + return (FALSE); + break; + case OP_CB_LAYOUTRECALL: + if (!xdr_CB_LAYOUTRECALL4res(xdrs, + &objp->nfs_cb_resop4_u.opcblayoutrecall)) + return (FALSE); + break; + case OP_CB_NOTIFY: + if (!xdr_CB_NOTIFY4res(xdrs, + &objp->nfs_cb_resop4_u.opcbnotify)) + return (FALSE); + break; + case OP_CB_PUSH_DELEG: + if (!xdr_CB_PUSH_DELEG4res(xdrs, + &objp->nfs_cb_resop4_u.opcbpush_deleg)) + return (FALSE); + break; + case OP_CB_RECALL_ANY: + if (!xdr_CB_RECALL_ANY4res(xdrs, + &objp->nfs_cb_resop4_u.opcbrecall_any)) + return (FALSE); + break; + case OP_CB_RECALLABLE_OBJ_AVAIL: + if (!xdr_CB_RECALLABLE_OBJ_AVAIL4res(xdrs, + &objp->nfs_cb_resop4_u.opcbrecallable_obj_avail)) + return (FALSE); + break; + case OP_CB_RECALL_SLOT: + if (!xdr_CB_RECALL_SLOT4res(xdrs, + &objp->nfs_cb_resop4_u.opcbrecall_slot)) + return (FALSE); + break; + case OP_CB_SEQUENCE: + if (!xdr_CB_SEQUENCE4res(xdrs, + &objp->nfs_cb_resop4_u.opcbsequence)) + return (FALSE); + break; + case OP_CB_WANTS_CANCELLED: + if (!xdr_CB_WANTS_CANCELLED4res(xdrs, + &objp->nfs_cb_resop4_u.opcbwants_cancelled)) + return (FALSE); + break; + case OP_CB_NOTIFY_LOCK: + if (!xdr_CB_NOTIFY_LOCK4res(xdrs, + &objp->nfs_cb_resop4_u.opcbnotify_lock)) + return (FALSE); + break; + case OP_CB_NOTIFY_DEVICEID: + if (!xdr_CB_NOTIFY_DEVICEID4res(xdrs, + &objp->nfs_cb_resop4_u.opcbnotify_deviceid)) + return (FALSE); + break; + case OP_CB_ILLEGAL: + if (!xdr_CB_ILLEGAL4res(xdrs, + &objp->nfs_cb_resop4_u.opcbillegal)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +/* + * Additional common NFSv4 XDR + */ + +bool_t +xdr_clientid4(XDR *xdrs, clientid4 *objp) +{ + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_component4(XDR *xdrs, component4 *objp) +{ + if (!xdr_utf8string(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_count4(XDR *xdrs, count4 *objp) +{ + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fsid4(XDR *xdrs, fsid4 *objp) +{ + if (!xdr_uint64_t(xdrs, &objp->major)) + return (FALSE); + if (!xdr_uint64_t(xdrs, &objp->minor)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_length4(XDR *xdrs, length4 *objp) +{ + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_limit_by4(XDR *xdrs, limit_by4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_lock_owner4(XDR *xdrs, lock_owner4 *objp) +{ + if (!xdr_clientid4(xdrs, &objp->clientid)) + return (FALSE); + if (!xdr_bytes(xdrs, (char **)&objp->owner_val, + (uint_t *)&objp->owner_len, NFS4_OPAQUE_LIMIT)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_mode4(XDR *xdrs, mode4 *objp) +{ + if (!xdr_uint32_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_netaddr4(XDR *xdrs, netaddr4 *objp) +{ + if (!xdr_string(xdrs, &objp->na_r_netid, ~0)) + return (FALSE); + if (!xdr_string(xdrs, &objp->na_r_addr, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_nfs_cookie4(XDR *xdrs, nfs_cookie4 *objp) +{ + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + + +bool_t +xdr_nfs_modified_limit4(XDR *xdrs, nfs_modified_limit4 *objp) +{ + if (!xdr_uint32_t(xdrs, &objp->num_blocks)) + return (FALSE); + if (!xdr_uint32_t(xdrs, &objp->bytes_per_block)) + return (FALSE); + return (TRUE); +} + + +bool_t +xdr_nfs_space_limit4(XDR *xdrs, nfs_space_limit4 *objp) +{ + if (!xdr_limit_by4(xdrs, &objp->limitby)) + return (FALSE); + switch (objp->limitby) { + case NFS_LIMIT_SIZE: + if (!xdr_uint64_t(xdrs, &objp->nfs_space_limit4_u.filesize)) + return (FALSE); + break; + case NFS_LIMIT_BLOCKS: + if (!xdr_nfs_modified_limit4(xdrs, + &objp->nfs_space_limit4_u.mod_blocks)) + return (FALSE); + break; + default: + return (FALSE); + } + return (TRUE); +} + +bool_t +xdr_nfsstat4(XDR *xdrs, nfsstat4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_offset4(XDR *xdrs, offset4 *objp) +{ + if (!xdr_uint64_t(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_open_claim_type4(XDR *xdrs, open_claim_type4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_open_delegation_type4(XDR *xdrs, open_delegation_type4 *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_open_read_delegation4(XDR *xdrs, open_read_delegation4 *objp) +{ + if (!xdr_stateid4(xdrs, &objp->stateid)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->recall)) + return (FALSE); + if (!xdr_nfsace4(xdrs, &objp->permissions)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_open_write_delegation4(XDR *xdrs, open_write_delegation4 *objp) +{ + if (!xdr_stateid4(xdrs, &objp->stateid)) + return (FALSE); + if (!xdr_bool(xdrs, &objp->recall)) + return (FALSE); + if (!xdr_nfs_space_limit4(xdrs, &objp->space_limit)) + return (FALSE); + if (!xdr_nfsace4(xdrs, &objp->permissions)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_pathname4(XDR *xdrs, pathname4 *objp) +{ + if (!xdr_array(xdrs, (char **)&objp->pathname4_val, + (uint_t *)&objp->pathname4_len, ~0, sizeof (component4), + (xdrproc_t)xdr_component4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_sec_oid4(XDR *xdrs, sec_oid4 *objp) +{ + if (!xdr_bytes(xdrs, (char **)&objp->sec_oid4_val, + (uint_t *)&objp->sec_oid4_len, ~0)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_rpc_gss_svc_t(XDR *xdrs, rpc_gss_svc_t *objp) +{ + if (!xdr_enum(xdrs, (enum_t *)objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_stateid4(XDR *xdrs, stateid4 *objp) +{ + if (!xdr_uint32_t(xdrs, &objp->seqid)) + return (FALSE); + if (!xdr_opaque(xdrs, objp->other, 12)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_utf8str_cis(XDR *xdrs, utf8str_cis *objp) +{ + if (!xdr_utf8string(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_utf8str_cs(XDR *xdrs, utf8str_cs *objp) +{ + if (!xdr_utf8string(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +/* End of additional common NFSv4 XDR */ diff --git a/usr/src/uts/common/fs/nfs/nfs_server.c b/usr/src/uts/common/fs/nfs/nfs_server.c index b916f727d9..0071fe50c8 100644 --- a/usr/src/uts/common/fs/nfs/nfs_server.c +++ b/usr/src/uts/common/fs/nfs/nfs_server.c @@ -261,7 +261,6 @@ static SVC_CALLOUT_TABLE nfs_sct_rdma = { */ nvlist_t *rfs4_dss_paths, *rfs4_dss_oldpaths; -int rfs4_dispatch(struct rpcdisp *, struct svc_req *, SVCXPRT *, char *); bool_t rfs4_minorvers_mismatch(struct svc_req *, SVCXPRT *, void *); /* @@ -1056,13 +1055,13 @@ static struct rpcdisp rfsdisptab_v4[] = { */ /* RFS_NULL = 0 */ - {rpc_null, + {NULL, xdr_void, NULL_xdrproc_t, 0, xdr_void, NULL_xdrproc_t, 0, nullfree, RPC_IDEMPOTENT, 0}, /* RFS4_compound = 1 */ - {rfs4_compound, + {NULL, xdr_COMPOUND4args_srv, NULL_xdrproc_t, sizeof (COMPOUND4args), xdr_COMPOUND4res_srv, NULL_xdrproc_t, sizeof (COMPOUND4res), rfs4_compound_free, 0, 0}, diff --git a/usr/src/uts/common/fs/nfs/nfs_stats.c b/usr/src/uts/common/fs/nfs/nfs_stats.c index 97f820d756..6dc19f67d3 100644 --- a/usr/src/uts/common/fs/nfs/nfs_stats.c +++ b/usr/src/uts/common/fs/nfs/nfs_stats.c @@ -21,6 +21,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2020 RackTop Systems, Inc. */ #include <sys/types.h> @@ -417,6 +419,25 @@ static const kstat_named_t rfsproccnt_v4_tmpl[] = { { "verify", KSTAT_DATA_UINT64 }, { "write", KSTAT_DATA_UINT64 }, { "release_lockowner", KSTAT_DATA_UINT64 }, + { "backchannel_ctl", KSTAT_DATA_UINT64 }, + { "bind_conn_to_session", KSTAT_DATA_UINT64 }, + { "exchange_id", KSTAT_DATA_UINT64 }, + { "create_session", KSTAT_DATA_UINT64 }, + { "destroy_session", KSTAT_DATA_UINT64 }, + { "free_stateid", KSTAT_DATA_UINT64 }, + { "get_dir_delegation", KSTAT_DATA_UINT64 }, + { "getdeviceinfo", KSTAT_DATA_UINT64 }, + { "getdevicelist", KSTAT_DATA_UINT64 }, + { "layoutcommit", KSTAT_DATA_UINT64 }, + { "layoutget", KSTAT_DATA_UINT64 }, + { "layoutreturn", KSTAT_DATA_UINT64 }, + { "secinfo_no_name", KSTAT_DATA_UINT64 }, + { "sequence", KSTAT_DATA_UINT64 }, + { "set_ssv", KSTAT_DATA_UINT64 }, + { "test_stateid", KSTAT_DATA_UINT64 }, + { "want_delegation", KSTAT_DATA_UINT64 }, + { "destroy_clientid", KSTAT_DATA_UINT64 }, + { "reclaim_complete", KSTAT_DATA_UINT64 }, { "illegal", KSTAT_DATA_UINT64 }, }; diff --git a/usr/src/uts/common/fs/nfs/nfs_subr.c b/usr/src/uts/common/fs/nfs/nfs_subr.c index 68cd0df081..ee622988ab 100644 --- a/usr/src/uts/common/fs/nfs/nfs_subr.c +++ b/usr/src/uts/common/fs/nfs/nfs_subr.c @@ -5277,3 +5277,13 @@ do_xattr_exists_check(vnode_t *vp, ulong_t *valp, cred_t *cr) kmem_free(dbuf, dlen); return (0); } + +/* + * NFS specific function that returns time since + * system boot in seconds. + */ +time_t +nfs_sys_uptime(void) +{ + return (TICK_TO_SEC(ddi_get_lbolt())); +} |
