nfs_srvsubs.c revision 182371
1139823Simp/*-
21541Srgrimes * Copyright (c) 1989, 1993
31541Srgrimes *	The Regents of the University of California.  All rights reserved.
41541Srgrimes *
51541Srgrimes * This code is derived from software contributed to Berkeley by
61541Srgrimes * Rick Macklem at The University of Guelph.
71541Srgrimes *
81541Srgrimes * Redistribution and use in source and binary forms, with or without
91541Srgrimes * modification, are permitted provided that the following conditions
101541Srgrimes * are met:
111541Srgrimes * 1. Redistributions of source code must retain the above copyright
121541Srgrimes *    notice, this list of conditions and the following disclaimer.
131541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141541Srgrimes *    notice, this list of conditions and the following disclaimer in the
151541Srgrimes *    documentation and/or other materials provided with the distribution.
161541Srgrimes * 4. Neither the name of the University nor the names of its contributors
171541Srgrimes *    may be used to endorse or promote products derived from this software
181541Srgrimes *    without specific prior written permission.
191541Srgrimes *
201541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301541Srgrimes * SUCH DAMAGE.
311541Srgrimes *
3236503Speter *	@(#)nfs_subs.c  8.8 (Berkeley) 5/22/95
331541Srgrimes */
341541Srgrimes
3583651Speter#include <sys/cdefs.h>
3683651Speter__FBSDID("$FreeBSD: head/sys/nfsserver/nfs_srvsubs.c 182371 2008-08-28 15:23:18Z attilio $");
3783651Speter
381541Srgrimes/*
391541Srgrimes * These functions support the macros and help fiddle mbuf chains for
401541Srgrimes * the nfs op functions. They do things like create the rpc header and
411541Srgrimes * copy data between mbuf chains and uio lists.
421541Srgrimes */
4383651Speter
44100134Salfred#include "opt_inet6.h"
45100134Salfred
461541Srgrimes#include <sys/param.h>
4748274Speter#include <sys/systm.h>
4848274Speter#include <sys/kernel.h>
4960041Sphk#include <sys/bio.h>
5031886Sbde#include <sys/buf.h>
511541Srgrimes#include <sys/proc.h>
521541Srgrimes#include <sys/mount.h>
531541Srgrimes#include <sys/vnode.h>
541541Srgrimes#include <sys/namei.h>
551541Srgrimes#include <sys/mbuf.h>
56150634Sjhb#include <sys/refcount.h>
571541Srgrimes#include <sys/socket.h>
581541Srgrimes#include <sys/stat.h>
599336Sdfr#include <sys/malloc.h>
6083700Speter#include <sys/module.h>
612997Swollman#include <sys/sysent.h>
622997Swollman#include <sys/syscall.h>
6383651Speter#include <sys/sysproto.h>
641541Srgrimes
653305Sphk#include <vm/vm.h>
6612662Sdg#include <vm/vm_object.h>
6712662Sdg#include <vm/vm_extern.h>
6892783Sjeff#include <vm/uma.h>
693305Sphk
701541Srgrimes#include <nfs/rpcv2.h>
719336Sdfr#include <nfs/nfsproto.h>
7283651Speter#include <nfsserver/nfs.h>
731541Srgrimes#include <nfs/xdr_subs.h>
7483651Speter#include <nfsserver/nfsm_subs.h>
751541Srgrimes
761541Srgrimes#include <netinet/in.h>
771541Srgrimes
781541Srgrimes/*
791541Srgrimes * Data items converted to xdr at startup, since they are constant
801541Srgrimes * This is kinda hokey, but may save a little time doing byte swaps
811541Srgrimes */
8289094Smsmithu_int32_t nfsrv_nfs_xdrneg1;
8389094Smsmithu_int32_t nfsrv_rpc_call, nfsrv_rpc_vers, nfsrv_rpc_reply,
8489094Smsmith	nfsrv_rpc_msgdenied, nfsrv_rpc_autherr,
8589094Smsmith	nfsrv_rpc_mismatch, nfsrv_rpc_auth_unix, nfsrv_rpc_msgaccepted;
8689094Smsmithu_int32_t nfsrv_nfs_prog, nfsrv_nfs_true, nfsrv_nfs_false;
871541Srgrimes
881541Srgrimes/* And other global data */
89129639Srwatsonstatic const nfstype nfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR,
90129639Srwatson				       NFLNK, NFNON, NFCHR, NFNON };
9183651Speter#define vtonfsv2_type(a)	txdr_unsigned(nfsv2_type[((int32_t)(a))])
9283651Speter#define vtonfsv3_mode(m)	txdr_unsigned((m) & ALLPERMS)
9312911Sphk
9489094Smsmithint nfsrv_ticks;
959336Sdfr
969759Sbdestruct nfssvc_sockhead nfssvc_sockhead;
979759Sbdeint nfssvc_sockhead_flag;
989759Sbdestruct nfsd_head nfsd_head;
999759Sbdeint nfsd_head_flag;
1009759Sbde
101168268Sjhbstatic int nfssvc_offset = SYS_nfssvc;
102168268Sjhbstatic struct sysent nfssvc_prev_sysent;
103168268SjhbMAKE_SYSENT(nfssvc);
10438894Sbde
105129639Srwatsonstruct mtx nfsd_mtx;
106129639Srwatson
1079336Sdfr/*
1089336Sdfr * Mapping of old NFS Version 2 RPC numbers to generic numbers.
1099336Sdfr */
110129639Srwatsonconst int nfsrv_nfsv3_procid[NFS_NPROCS] = {
1119336Sdfr	NFSPROC_NULL,
1129336Sdfr	NFSPROC_GETATTR,
1139336Sdfr	NFSPROC_SETATTR,
1149336Sdfr	NFSPROC_NOOP,
1159336Sdfr	NFSPROC_LOOKUP,
1169336Sdfr	NFSPROC_READLINK,
1179336Sdfr	NFSPROC_READ,
1189336Sdfr	NFSPROC_NOOP,
1199336Sdfr	NFSPROC_WRITE,
1209336Sdfr	NFSPROC_CREATE,
1219336Sdfr	NFSPROC_REMOVE,
1229336Sdfr	NFSPROC_RENAME,
1239336Sdfr	NFSPROC_LINK,
1249336Sdfr	NFSPROC_SYMLINK,
1259336Sdfr	NFSPROC_MKDIR,
1269336Sdfr	NFSPROC_RMDIR,
1279336Sdfr	NFSPROC_READDIR,
1289336Sdfr	NFSPROC_FSSTAT,
1299336Sdfr	NFSPROC_NOOP,
1309336Sdfr	NFSPROC_NOOP,
1319336Sdfr	NFSPROC_NOOP,
1329336Sdfr	NFSPROC_NOOP,
1339336Sdfr	NFSPROC_NOOP,
1349336Sdfr};
1359336Sdfr
1369336Sdfr/*
1379336Sdfr * and the reverse mapping from generic to Version 2 procedure numbers
1389336Sdfr */
139129639Srwatsonconst int nfsrvv2_procid[NFS_NPROCS] = {
1409336Sdfr	NFSV2PROC_NULL,
1419336Sdfr	NFSV2PROC_GETATTR,
1429336Sdfr	NFSV2PROC_SETATTR,
1439336Sdfr	NFSV2PROC_LOOKUP,
1449336Sdfr	NFSV2PROC_NOOP,
1459336Sdfr	NFSV2PROC_READLINK,
1469336Sdfr	NFSV2PROC_READ,
1479336Sdfr	NFSV2PROC_WRITE,
1489336Sdfr	NFSV2PROC_CREATE,
1499336Sdfr	NFSV2PROC_MKDIR,
1509336Sdfr	NFSV2PROC_SYMLINK,
1519336Sdfr	NFSV2PROC_CREATE,
1529336Sdfr	NFSV2PROC_REMOVE,
1539336Sdfr	NFSV2PROC_RMDIR,
1549336Sdfr	NFSV2PROC_RENAME,
1559336Sdfr	NFSV2PROC_LINK,
1569336Sdfr	NFSV2PROC_READDIR,
1579336Sdfr	NFSV2PROC_NOOP,
1589336Sdfr	NFSV2PROC_STATFS,
1599336Sdfr	NFSV2PROC_NOOP,
1609336Sdfr	NFSV2PROC_NOOP,
1619336Sdfr	NFSV2PROC_NOOP,
1629336Sdfr	NFSV2PROC_NOOP,
1639336Sdfr};
1649336Sdfr
1659336Sdfr/*
1669336Sdfr * Maps errno values to nfs error numbers.
167102236Sphk * Use 0 (which gets converted to NFSERR_IO) as the catch all for ones not
168102236Sphk * specifically defined in RFC 1094.
1699336Sdfr */
170129639Srwatsonstatic const u_char nfsrv_v2errmap[ELAST] = {
171102236Sphk  NFSERR_PERM,	NFSERR_NOENT,	0,		0,		0,
172102236Sphk  NFSERR_NXIO,	0,		0,		0,		0,
173102236Sphk  0,		0,		NFSERR_ACCES,	0,		0,
174102236Sphk  0,		NFSERR_EXIST,	0,		NFSERR_NODEV,	NFSERR_NOTDIR,
175102236Sphk  NFSERR_ISDIR,	0,		0,		0,		0,
176102236Sphk  0,		NFSERR_FBIG,	NFSERR_NOSPC,	0,		NFSERR_ROFS,
177102236Sphk  0,		0,		0,		0,		0,
178102236Sphk  0,		0,		0,		0,		0,
179102236Sphk  0,		0,		0,		0,		0,
180102236Sphk  0,		0,		0,		0,		0,
181102236Sphk  0,		0,		0,		0,		0,
182102236Sphk  0,		0,		0,		0,		0,
183102236Sphk  0,		0,		NFSERR_NAMETOL,	0,		0,
184102236Sphk  NFSERR_NOTEMPTY, 0,		0,		NFSERR_DQUOT,	NFSERR_STALE,
185102236Sphk  0
1869336Sdfr};
1879336Sdfr
1889336Sdfr/*
1899336Sdfr * Maps errno values to nfs error numbers.
1909336Sdfr * Although it is not obvious whether or not NFS clients really care if
1919336Sdfr * a returned error value is in the specified list for the procedure, the
1929336Sdfr * safest thing to do is filter them appropriately. For Version 2, the
1939336Sdfr * X/Open XNFS document is the only specification that defines error values
1949336Sdfr * for each RPC (The RFC simply lists all possible error values for all RPCs),
1959336Sdfr * so I have decided to not do this for Version 2.
1969336Sdfr * The first entry is the default error return and the rest are the valid
1979336Sdfr * errors for that RPC in increasing numeric order.
1989336Sdfr */
199129639Srwatsonstatic const short nfsv3err_null[] = {
2009336Sdfr	0,
2019336Sdfr	0,
2029336Sdfr};
2039336Sdfr
204129639Srwatsonstatic const short nfsv3err_getattr[] = {
2059336Sdfr	NFSERR_IO,
2069336Sdfr	NFSERR_IO,
2079336Sdfr	NFSERR_STALE,
2089336Sdfr	NFSERR_BADHANDLE,
2099336Sdfr	NFSERR_SERVERFAULT,
2109336Sdfr	0,
2119336Sdfr};
2129336Sdfr
213129639Srwatsonstatic const short nfsv3err_setattr[] = {
2149336Sdfr	NFSERR_IO,
2159336Sdfr	NFSERR_PERM,
2169336Sdfr	NFSERR_IO,
2179336Sdfr	NFSERR_ACCES,
2189336Sdfr	NFSERR_INVAL,
2199336Sdfr	NFSERR_NOSPC,
2209336Sdfr	NFSERR_ROFS,
2219336Sdfr	NFSERR_DQUOT,
2229336Sdfr	NFSERR_STALE,
2239336Sdfr	NFSERR_BADHANDLE,
2249336Sdfr	NFSERR_NOT_SYNC,
2259336Sdfr	NFSERR_SERVERFAULT,
2269336Sdfr	0,
2279336Sdfr};
2289336Sdfr
229129639Srwatsonstatic const short nfsv3err_lookup[] = {
2309336Sdfr	NFSERR_IO,
2319336Sdfr	NFSERR_NOENT,
2329336Sdfr	NFSERR_IO,
2339336Sdfr	NFSERR_ACCES,
2349336Sdfr	NFSERR_NOTDIR,
2359336Sdfr	NFSERR_NAMETOL,
2369336Sdfr	NFSERR_STALE,
2379336Sdfr	NFSERR_BADHANDLE,
2389336Sdfr	NFSERR_SERVERFAULT,
2399336Sdfr	0,
2409336Sdfr};
2419336Sdfr
242129639Srwatsonstatic const short nfsv3err_access[] = {
2439336Sdfr	NFSERR_IO,
2449336Sdfr	NFSERR_IO,
2459336Sdfr	NFSERR_STALE,
2469336Sdfr	NFSERR_BADHANDLE,
2479336Sdfr	NFSERR_SERVERFAULT,
2489336Sdfr	0,
2499336Sdfr};
2509336Sdfr
251129639Srwatsonstatic const short nfsv3err_readlink[] = {
2529336Sdfr	NFSERR_IO,
2539336Sdfr	NFSERR_IO,
2549336Sdfr	NFSERR_ACCES,
2559336Sdfr	NFSERR_INVAL,
2569336Sdfr	NFSERR_STALE,
2579336Sdfr	NFSERR_BADHANDLE,
2589336Sdfr	NFSERR_NOTSUPP,
2599336Sdfr	NFSERR_SERVERFAULT,
2609336Sdfr	0,
2619336Sdfr};
2629336Sdfr
263129639Srwatsonstatic const short nfsv3err_read[] = {
2649336Sdfr	NFSERR_IO,
2659336Sdfr	NFSERR_IO,
2669336Sdfr	NFSERR_NXIO,
2679336Sdfr	NFSERR_ACCES,
2689336Sdfr	NFSERR_INVAL,
2699336Sdfr	NFSERR_STALE,
2709336Sdfr	NFSERR_BADHANDLE,
2719336Sdfr	NFSERR_SERVERFAULT,
2729336Sdfr	0,
2739336Sdfr};
2749336Sdfr
275129639Srwatsonstatic const short nfsv3err_write[] = {
2769336Sdfr	NFSERR_IO,
2779336Sdfr	NFSERR_IO,
2789336Sdfr	NFSERR_ACCES,
2799336Sdfr	NFSERR_INVAL,
2809336Sdfr	NFSERR_FBIG,
2819336Sdfr	NFSERR_NOSPC,
2829336Sdfr	NFSERR_ROFS,
2839336Sdfr	NFSERR_DQUOT,
2849336Sdfr	NFSERR_STALE,
2859336Sdfr	NFSERR_BADHANDLE,
2869336Sdfr	NFSERR_SERVERFAULT,
2879336Sdfr	0,
2889336Sdfr};
2899336Sdfr
290129639Srwatsonstatic const short nfsv3err_create[] = {
2919336Sdfr	NFSERR_IO,
2929336Sdfr	NFSERR_IO,
2939336Sdfr	NFSERR_ACCES,
2949336Sdfr	NFSERR_EXIST,
2959336Sdfr	NFSERR_NOTDIR,
2969336Sdfr	NFSERR_NOSPC,
2979336Sdfr	NFSERR_ROFS,
2989336Sdfr	NFSERR_NAMETOL,
2999336Sdfr	NFSERR_DQUOT,
3009336Sdfr	NFSERR_STALE,
3019336Sdfr	NFSERR_BADHANDLE,
3029336Sdfr	NFSERR_NOTSUPP,
3039336Sdfr	NFSERR_SERVERFAULT,
3049336Sdfr	0,
3059336Sdfr};
3069336Sdfr
307129639Srwatsonstatic const short nfsv3err_mkdir[] = {
3089336Sdfr	NFSERR_IO,
3099336Sdfr	NFSERR_IO,
3109336Sdfr	NFSERR_ACCES,
3119336Sdfr	NFSERR_EXIST,
3129336Sdfr	NFSERR_NOTDIR,
3139336Sdfr	NFSERR_NOSPC,
3149336Sdfr	NFSERR_ROFS,
3159336Sdfr	NFSERR_NAMETOL,
3169336Sdfr	NFSERR_DQUOT,
3179336Sdfr	NFSERR_STALE,
3189336Sdfr	NFSERR_BADHANDLE,
3199336Sdfr	NFSERR_NOTSUPP,
3209336Sdfr	NFSERR_SERVERFAULT,
3219336Sdfr	0,
3229336Sdfr};
3239336Sdfr
324129639Srwatsonstatic const short nfsv3err_symlink[] = {
3259336Sdfr	NFSERR_IO,
3269336Sdfr	NFSERR_IO,
3279336Sdfr	NFSERR_ACCES,
3289336Sdfr	NFSERR_EXIST,
3299336Sdfr	NFSERR_NOTDIR,
3309336Sdfr	NFSERR_NOSPC,
3319336Sdfr	NFSERR_ROFS,
3329336Sdfr	NFSERR_NAMETOL,
3339336Sdfr	NFSERR_DQUOT,
3349336Sdfr	NFSERR_STALE,
3359336Sdfr	NFSERR_BADHANDLE,
3369336Sdfr	NFSERR_NOTSUPP,
3379336Sdfr	NFSERR_SERVERFAULT,
3389336Sdfr	0,
3399336Sdfr};
3409336Sdfr
341129639Srwatsonstatic const short nfsv3err_mknod[] = {
3429336Sdfr	NFSERR_IO,
3439336Sdfr	NFSERR_IO,
3449336Sdfr	NFSERR_ACCES,
3459336Sdfr	NFSERR_EXIST,
3469336Sdfr	NFSERR_NOTDIR,
3479336Sdfr	NFSERR_NOSPC,
3489336Sdfr	NFSERR_ROFS,
3499336Sdfr	NFSERR_NAMETOL,
3509336Sdfr	NFSERR_DQUOT,
3519336Sdfr	NFSERR_STALE,
3529336Sdfr	NFSERR_BADHANDLE,
3539336Sdfr	NFSERR_NOTSUPP,
3549336Sdfr	NFSERR_SERVERFAULT,
3559336Sdfr	NFSERR_BADTYPE,
3569336Sdfr	0,
3579336Sdfr};
3589336Sdfr
359129639Srwatsonstatic const short nfsv3err_remove[] = {
3609336Sdfr	NFSERR_IO,
3619336Sdfr	NFSERR_NOENT,
3629336Sdfr	NFSERR_IO,
3639336Sdfr	NFSERR_ACCES,
3649336Sdfr	NFSERR_NOTDIR,
3659336Sdfr	NFSERR_ROFS,
3669336Sdfr	NFSERR_NAMETOL,
3679336Sdfr	NFSERR_STALE,
3689336Sdfr	NFSERR_BADHANDLE,
3699336Sdfr	NFSERR_SERVERFAULT,
3709336Sdfr	0,
3719336Sdfr};
3729336Sdfr
373129639Srwatsonstatic const short nfsv3err_rmdir[] = {
3749336Sdfr	NFSERR_IO,
3759336Sdfr	NFSERR_NOENT,
3769336Sdfr	NFSERR_IO,
3779336Sdfr	NFSERR_ACCES,
3789336Sdfr	NFSERR_EXIST,
3799336Sdfr	NFSERR_NOTDIR,
3809336Sdfr	NFSERR_INVAL,
3819336Sdfr	NFSERR_ROFS,
3829336Sdfr	NFSERR_NAMETOL,
3839336Sdfr	NFSERR_NOTEMPTY,
3849336Sdfr	NFSERR_STALE,
3859336Sdfr	NFSERR_BADHANDLE,
3869336Sdfr	NFSERR_NOTSUPP,
3879336Sdfr	NFSERR_SERVERFAULT,
3889336Sdfr	0,
3899336Sdfr};
3909336Sdfr
391129639Srwatsonstatic const short nfsv3err_rename[] = {
3929336Sdfr	NFSERR_IO,
3939336Sdfr	NFSERR_NOENT,
3949336Sdfr	NFSERR_IO,
3959336Sdfr	NFSERR_ACCES,
3969336Sdfr	NFSERR_EXIST,
3979336Sdfr	NFSERR_XDEV,
3989336Sdfr	NFSERR_NOTDIR,
3999336Sdfr	NFSERR_ISDIR,
4009336Sdfr	NFSERR_INVAL,
4019336Sdfr	NFSERR_NOSPC,
4029336Sdfr	NFSERR_ROFS,
4039336Sdfr	NFSERR_MLINK,
4049336Sdfr	NFSERR_NAMETOL,
4059336Sdfr	NFSERR_NOTEMPTY,
4069336Sdfr	NFSERR_DQUOT,
4079336Sdfr	NFSERR_STALE,
4089336Sdfr	NFSERR_BADHANDLE,
4099336Sdfr	NFSERR_NOTSUPP,
4109336Sdfr	NFSERR_SERVERFAULT,
4119336Sdfr	0,
4129336Sdfr};
4139336Sdfr
414129639Srwatsonstatic const short nfsv3err_link[] = {
4159336Sdfr	NFSERR_IO,
4169336Sdfr	NFSERR_IO,
4179336Sdfr	NFSERR_ACCES,
4189336Sdfr	NFSERR_EXIST,
4199336Sdfr	NFSERR_XDEV,
4209336Sdfr	NFSERR_NOTDIR,
4219336Sdfr	NFSERR_INVAL,
4229336Sdfr	NFSERR_NOSPC,
4239336Sdfr	NFSERR_ROFS,
4249336Sdfr	NFSERR_MLINK,
4259336Sdfr	NFSERR_NAMETOL,
4269336Sdfr	NFSERR_DQUOT,
4279336Sdfr	NFSERR_STALE,
4289336Sdfr	NFSERR_BADHANDLE,
4299336Sdfr	NFSERR_NOTSUPP,
4309336Sdfr	NFSERR_SERVERFAULT,
4319336Sdfr	0,
4329336Sdfr};
4339336Sdfr
434129639Srwatsonstatic const short nfsv3err_readdir[] = {
4359336Sdfr	NFSERR_IO,
4369336Sdfr	NFSERR_IO,
4379336Sdfr	NFSERR_ACCES,
4389336Sdfr	NFSERR_NOTDIR,
4399336Sdfr	NFSERR_STALE,
4409336Sdfr	NFSERR_BADHANDLE,
4419336Sdfr	NFSERR_BAD_COOKIE,
4429336Sdfr	NFSERR_TOOSMALL,
4439336Sdfr	NFSERR_SERVERFAULT,
4449336Sdfr	0,
4459336Sdfr};
4469336Sdfr
447129639Srwatsonstatic const short nfsv3err_readdirplus[] = {
4489336Sdfr	NFSERR_IO,
4499336Sdfr	NFSERR_IO,
4509336Sdfr	NFSERR_ACCES,
4519336Sdfr	NFSERR_NOTDIR,
4529336Sdfr	NFSERR_STALE,
4539336Sdfr	NFSERR_BADHANDLE,
4549336Sdfr	NFSERR_BAD_COOKIE,
4559336Sdfr	NFSERR_NOTSUPP,
4569336Sdfr	NFSERR_TOOSMALL,
4579336Sdfr	NFSERR_SERVERFAULT,
4589336Sdfr	0,
4599336Sdfr};
4609336Sdfr
461129639Srwatsonstatic const short nfsv3err_fsstat[] = {
4629336Sdfr	NFSERR_IO,
4639336Sdfr	NFSERR_IO,
4649336Sdfr	NFSERR_STALE,
4659336Sdfr	NFSERR_BADHANDLE,
4669336Sdfr	NFSERR_SERVERFAULT,
4679336Sdfr	0,
4689336Sdfr};
4699336Sdfr
470129639Srwatsonstatic const short nfsv3err_fsinfo[] = {
4719336Sdfr	NFSERR_STALE,
4729336Sdfr	NFSERR_STALE,
4739336Sdfr	NFSERR_BADHANDLE,
4749336Sdfr	NFSERR_SERVERFAULT,
4759336Sdfr	0,
4769336Sdfr};
4779336Sdfr
478129639Srwatsonstatic const short nfsv3err_pathconf[] = {
4799336Sdfr	NFSERR_STALE,
4809336Sdfr	NFSERR_STALE,
4819336Sdfr	NFSERR_BADHANDLE,
4829336Sdfr	NFSERR_SERVERFAULT,
4839336Sdfr	0,
4849336Sdfr};
4859336Sdfr
486129639Srwatsonstatic const short nfsv3err_commit[] = {
4879336Sdfr	NFSERR_IO,
4889336Sdfr	NFSERR_IO,
4899336Sdfr	NFSERR_STALE,
4909336Sdfr	NFSERR_BADHANDLE,
4919336Sdfr	NFSERR_SERVERFAULT,
4929336Sdfr	0,
4939336Sdfr};
4949336Sdfr
495129639Srwatsonstatic const short *nfsrv_v3errmap[] = {
4969336Sdfr	nfsv3err_null,
4979336Sdfr	nfsv3err_getattr,
4989336Sdfr	nfsv3err_setattr,
4999336Sdfr	nfsv3err_lookup,
5009336Sdfr	nfsv3err_access,
5019336Sdfr	nfsv3err_readlink,
5029336Sdfr	nfsv3err_read,
5039336Sdfr	nfsv3err_write,
5049336Sdfr	nfsv3err_create,
5059336Sdfr	nfsv3err_mkdir,
5069336Sdfr	nfsv3err_symlink,
5079336Sdfr	nfsv3err_mknod,
5089336Sdfr	nfsv3err_remove,
5099336Sdfr	nfsv3err_rmdir,
5109336Sdfr	nfsv3err_rename,
5119336Sdfr	nfsv3err_link,
5129336Sdfr	nfsv3err_readdir,
5139336Sdfr	nfsv3err_readdirplus,
5149336Sdfr	nfsv3err_fsstat,
5159336Sdfr	nfsv3err_fsinfo,
5169336Sdfr	nfsv3err_pathconf,
5179336Sdfr	nfsv3err_commit,
5189336Sdfr};
5199336Sdfr
5201541Srgrimes/*
5211541Srgrimes * Called once to initialize data structures...
5221541Srgrimes */
52383651Speterstatic int
52483700Speternfsrv_modevent(module_t mod, int type, void *data)
5251541Srgrimes{
526168268Sjhb	static int registered;
527132199Sphk	int error = 0;
5281541Srgrimes
52983700Speter	switch (type) {
53083700Speter	case MOD_LOAD:
531129639Srwatson		mtx_init(&nfsd_mtx, "nfsd_mtx", NULL, MTX_DEF);
53289094Smsmith		nfsrv_rpc_vers = txdr_unsigned(RPC_VER2);
53389094Smsmith		nfsrv_rpc_call = txdr_unsigned(RPC_CALL);
53489094Smsmith		nfsrv_rpc_reply = txdr_unsigned(RPC_REPLY);
53589094Smsmith		nfsrv_rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
53689094Smsmith		nfsrv_rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
53789094Smsmith		nfsrv_rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
53889094Smsmith		nfsrv_rpc_autherr = txdr_unsigned(RPC_AUTHERR);
53989094Smsmith		nfsrv_rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
54089094Smsmith		nfsrv_nfs_prog = txdr_unsigned(NFS_PROG);
54189094Smsmith		nfsrv_nfs_true = txdr_unsigned(TRUE);
54289094Smsmith		nfsrv_nfs_false = txdr_unsigned(FALSE);
54389094Smsmith		nfsrv_nfs_xdrneg1 = txdr_unsigned(-1);
54489094Smsmith		nfsrv_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
54589094Smsmith		if (nfsrv_ticks < 1)
54689094Smsmith			nfsrv_ticks = 1;
54783651Speter
548129639Srwatson		nfsrv_initcache();	/* Init the server request cache */
549129639Srwatson		NFSD_LOCK();
55083700Speter		nfsrv_init(0);		/* Init server data structures */
551171613Srwatson		callout_init(&nfsrv_callout, CALLOUT_MPSAFE);
552129639Srwatson		NFSD_UNLOCK();
55383700Speter		nfsrv_timer(0);
5541541Srgrimes
555168268Sjhb		error = syscall_register(&nfssvc_offset, &nfssvc_sysent,
556168268Sjhb		    &nfssvc_prev_sysent);
557168268Sjhb		if (error)
558168268Sjhb			break;
559168268Sjhb		registered = 1;
56083700Speter		break;
5612997Swollman
562132199Sphk	case MOD_UNLOAD:
563129902Sbmilekic		if (nfsrv_numnfsd != 0) {
564132199Sphk			error = EBUSY;
565132199Sphk			break;
566129902Sbmilekic		}
56783700Speter
568168268Sjhb		if (registered)
569168268Sjhb			syscall_deregister(&nfssvc_offset, &nfssvc_prev_sysent);
570160881Sjhb		callout_drain(&nfsrv_callout);
571160881Sjhb		nfsrv_destroycache();	/* Free the server request cache */
572129639Srwatson		mtx_destroy(&nfsd_mtx);
57383700Speter		break;
574132199Sphk	default:
575132199Sphk		error = EOPNOTSUPP;
576132199Sphk		break;
57783700Speter	}
578132199Sphk	return error;
5791541Srgrimes}
58083700Speterstatic moduledata_t nfsserver_mod = {
58183700Speter	"nfsserver",
58283700Speter	nfsrv_modevent,
58383700Speter	NULL,
58483700Speter};
58583700SpeterDECLARE_MODULE(nfsserver, nfsserver_mod, SI_SUB_VFS, SI_ORDER_ANY);
5861541Srgrimes
58783700Speter/* So that loader and kldload(2) can find us, wherever we are.. */
58883700SpeterMODULE_VERSION(nfsserver, 1);
58938894Sbde
5901541Srgrimes/*
59127446Sdfr * Set up nameidata for a lookup() call and do it.
59227446Sdfr *
59327446Sdfr * If pubflag is set, this call is done for a lookup operation on the
59427446Sdfr * public filehandle. In that case we allow crossing mountpoints and
59527446Sdfr * absolute pathnames. However, the caller is expected to check that
59627446Sdfr * the lookup result is within the public fs, and deny access if
59727446Sdfr * it is not.
59848125Sjulian *
59948125Sjulian * nfs_namei() clears out garbage fields that namei() might leave garbage.
60048125Sjulian * This is mainly ni_vp and ni_dvp when an error occurs, and ni_dvp when no
60148125Sjulian * error occurs but the parent was not requested.
60248125Sjulian *
60383651Speter * dirp may be set whether an error is returned or not, and must be
60448125Sjulian * released by the caller.
6051541Srgrimes */
6061549Srgrimesint
60783651Speternfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
60883651Speter    struct nfssvc_sock *slp, struct sockaddr *nam, struct mbuf **mdp,
609115301Struckman    caddr_t *dposp, struct vnode **retdirp, int v3, struct vattr *retdirattrp,
610115301Struckman    int *retdirattr_retp, struct thread *td, int pubflag)
6111541Srgrimes{
61283651Speter	int i, rem;
61383651Speter	struct mbuf *md;
61483651Speter	char *fromcp, *tocp, *cp;
61527446Sdfr	struct iovec aiov;
61627446Sdfr	struct uio auio;
6171541Srgrimes	struct vnode *dp;
61827446Sdfr	int error, rdonly, linklen;
6191541Srgrimes	struct componentname *cnp = &ndp->ni_cnd;
620115301Struckman	int lockleaf = (cnp->cn_flags & LOCKLEAF) != 0;
621167665Sjeff	int dvfslocked;
622167665Sjeff	int vfslocked;
6231541Srgrimes
624167665Sjeff	vfslocked = 0;
625167665Sjeff	dvfslocked = 0;
62699797Sdillon	*retdirp = NULL;
627105481Srwatson	cnp->cn_flags |= NOMACCHECK;
628111119Simp	cnp->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
62929653Sdyson
6301541Srgrimes	/*
6311541Srgrimes	 * Copy the name from the mbuf list to ndp->ni_pnbuf
6321541Srgrimes	 * and set the various ndp fields appropriately.
6331541Srgrimes	 */
6341541Srgrimes	fromcp = *dposp;
6351541Srgrimes	tocp = cnp->cn_pnbuf;
6361541Srgrimes	md = *mdp;
6371541Srgrimes	rem = mtod(md, caddr_t) + md->m_len - fromcp;
6381541Srgrimes	for (i = 0; i < len; i++) {
6391541Srgrimes		while (rem == 0) {
6401541Srgrimes			md = md->m_next;
6411541Srgrimes			if (md == NULL) {
6421541Srgrimes				error = EBADRPC;
643167665Sjeff				goto out;
6441541Srgrimes			}
6451541Srgrimes			fromcp = mtod(md, caddr_t);
6461541Srgrimes			rem = md->m_len;
6471541Srgrimes		}
64827446Sdfr		if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) {
6499336Sdfr			error = EACCES;
650167665Sjeff			goto out;
6511541Srgrimes		}
6521541Srgrimes		*tocp++ = *fromcp++;
6531541Srgrimes		rem--;
6541541Srgrimes	}
6551541Srgrimes	*tocp = '\0';
6561541Srgrimes	*mdp = md;
6571541Srgrimes	*dposp = fromcp;
6581541Srgrimes	len = nfsm_rndup(len)-len;
6591541Srgrimes	if (len > 0) {
6601541Srgrimes		if (rem >= len)
6611541Srgrimes			*dposp += len;
66227609Sdfr		else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
663167665Sjeff			goto out;
6641541Srgrimes	}
66527446Sdfr
6661541Srgrimes	/*
6671541Srgrimes	 * Extract and set starting directory.
6681541Srgrimes	 */
669167665Sjeff	error = nfsrv_fhtovp(fhp, FALSE, &dp, &dvfslocked,
670167665Sjeff	    ndp->ni_cnd.cn_cred, slp, nam, &rdonly, pubflag);
67127446Sdfr	if (error)
6721541Srgrimes		goto out;
673167665Sjeff	vfslocked = VFS_LOCK_GIANT(dp->v_mount);
6741541Srgrimes	if (dp->v_type != VDIR) {
67517761Sdyson		vrele(dp);
6761541Srgrimes		error = ENOTDIR;
6771541Srgrimes		goto out;
6781541Srgrimes	}
67927446Sdfr
68027446Sdfr	if (rdonly)
68127446Sdfr		cnp->cn_flags |= RDONLY;
68227446Sdfr
68348125Sjulian	/*
68483651Speter	 * Set return directory.  Reference to dp is implicitly transfered
68548125Sjulian	 * to the returned pointer
68648125Sjulian	 */
68727609Sdfr	*retdirp = dp;
688115301Struckman	if (v3) {
689175202Sattilio		vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
690115301Struckman		*retdirattr_retp = VOP_GETATTR(dp, retdirattrp,
691182371Sattilio			ndp->ni_cnd.cn_cred);
692175294Sattilio		VOP_UNLOCK(dp, 0);
693115301Struckman	}
69427609Sdfr
69527446Sdfr	if (pubflag) {
69627446Sdfr		/*
69727446Sdfr		 * Oh joy. For WebNFS, handle those pesky '%' escapes,
69827446Sdfr		 * and the 'native path' indicator.
69927446Sdfr		 */
700111119Simp		cp = uma_zalloc(namei_zone, M_WAITOK);
70127446Sdfr		fromcp = cnp->cn_pnbuf;
70227446Sdfr		tocp = cp;
70327446Sdfr		if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) {
70427446Sdfr			switch ((unsigned char)*fromcp) {
70527446Sdfr			case WEBNFS_NATIVE_CHAR:
70627446Sdfr				/*
70727446Sdfr				 * 'Native' path for us is the same
70827446Sdfr				 * as a path according to the NFS spec,
70927446Sdfr				 * just skip the escape char.
71027446Sdfr				 */
71127446Sdfr				fromcp++;
71227446Sdfr				break;
71327446Sdfr			/*
71427446Sdfr			 * More may be added in the future, range 0x80-0xff
71527446Sdfr			 */
71627446Sdfr			default:
71727446Sdfr				error = EIO;
71892783Sjeff				uma_zfree(namei_zone, cp);
71927446Sdfr				goto out;
72027446Sdfr			}
72127446Sdfr		}
72227446Sdfr		/*
72327446Sdfr		 * Translate the '%' escapes, URL-style.
72427446Sdfr		 */
72527446Sdfr		while (*fromcp != '\0') {
72627446Sdfr			if (*fromcp == WEBNFS_ESC_CHAR) {
72727446Sdfr				if (fromcp[1] != '\0' && fromcp[2] != '\0') {
72827446Sdfr					fromcp++;
72927446Sdfr					*tocp++ = HEXSTRTOI(fromcp);
73027446Sdfr					fromcp += 2;
73127446Sdfr					continue;
73227446Sdfr				} else {
73327446Sdfr					error = ENOENT;
73492783Sjeff					uma_zfree(namei_zone, cp);
73527446Sdfr					goto out;
73627446Sdfr				}
73727446Sdfr			} else
73827446Sdfr				*tocp++ = *fromcp++;
73927446Sdfr		}
74027446Sdfr		*tocp = '\0';
74192783Sjeff		uma_zfree(namei_zone, cnp->cn_pnbuf);
74227446Sdfr		cnp->cn_pnbuf = cp;
74327446Sdfr	}
74427446Sdfr
74527446Sdfr	ndp->ni_pathlen = (tocp - cnp->cn_pnbuf) + 1;
74627446Sdfr	ndp->ni_segflg = UIO_SYSSPACE;
74727446Sdfr
74827446Sdfr	if (pubflag) {
74927446Sdfr		ndp->ni_rootdir = rootvnode;
75027446Sdfr		ndp->ni_loopcnt = 0;
751167665Sjeff		if (cnp->cn_pnbuf[0] == '/') {
752167665Sjeff			int tvfslocked;
753167665Sjeff
754167665Sjeff			tvfslocked = VFS_LOCK_GIANT(rootvnode->v_mount);
755167665Sjeff			VFS_UNLOCK_GIANT(vfslocked);
75627446Sdfr			dp = rootvnode;
757167665Sjeff			vfslocked = tvfslocked;
758167665Sjeff		}
75927446Sdfr	} else {
76027609Sdfr		cnp->cn_flags |= NOCROSSMOUNT;
76127446Sdfr	}
76227446Sdfr
76348125Sjulian	/*
76448125Sjulian	 * Initialize for scan, set ni_startdir and bump ref on dp again
765123608Sjhb	 * because lookup() will dereference ni_startdir.
76648125Sjulian	 */
76748125Sjulian
76883366Sjulian	cnp->cn_thread = td;
7699336Sdfr	VREF(dp);
77048125Sjulian	ndp->ni_startdir = dp;
77127446Sdfr
772115301Struckman	if (!lockleaf)
773115301Struckman		cnp->cn_flags |= LOCKLEAF;
77448125Sjulian	for (;;) {
77548125Sjulian		cnp->cn_nameptr = cnp->cn_pnbuf;
77648125Sjulian		/*
77748125Sjulian		 * Call lookup() to do the real work.  If an error occurs,
77848125Sjulian		 * ndp->ni_vp and ni_dvp are left uninitialized or NULL and
77948125Sjulian		 * we do not have to dereference anything before returning.
78048125Sjulian		 * In either case ni_startdir will be dereferenced and NULLed
78148125Sjulian		 * out.
78248125Sjulian		 */
783167665Sjeff		if (vfslocked)
784167665Sjeff			ndp->ni_cnd.cn_flags |= GIANTHELD;
78548125Sjulian		error = lookup(ndp);
786167665Sjeff		vfslocked = (ndp->ni_cnd.cn_flags & GIANTHELD) != 0;
787167665Sjeff		ndp->ni_cnd.cn_flags &= ~GIANTHELD;
78848125Sjulian		if (error)
78948125Sjulian			break;
79048125Sjulian
79148125Sjulian		/*
79283651Speter		 * Check for encountering a symbolic link.  Trivial
79348125Sjulian		 * termination occurs if no symlink encountered.
79448125Sjulian		 * Note: zfree is safe because error is 0, so we will
79548125Sjulian		 * not zfree it again when we break.
79648125Sjulian		 */
79748125Sjulian		if ((cnp->cn_flags & ISSYMLINK) == 0) {
79848125Sjulian			if (cnp->cn_flags & (SAVENAME | SAVESTART))
79948125Sjulian				cnp->cn_flags |= HASBUF;
80048125Sjulian			else
80192783Sjeff				uma_zfree(namei_zone, cnp->cn_pnbuf);
802115301Struckman			if (ndp->ni_vp && !lockleaf)
803175294Sattilio				VOP_UNLOCK(ndp->ni_vp, 0);
80448125Sjulian			break;
80527446Sdfr		}
80648125Sjulian
80748125Sjulian		/*
80848125Sjulian		 * Validate symlink
80948125Sjulian		 */
8101541Srgrimes		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
811175294Sattilio			VOP_UNLOCK(ndp->ni_dvp, 0);
81227446Sdfr		if (!pubflag) {
81327446Sdfr			error = EINVAL;
81448125Sjulian			goto badlink2;
81527446Sdfr		}
81627446Sdfr
81727446Sdfr		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
81827446Sdfr			error = ELOOP;
81948125Sjulian			goto badlink2;
82027446Sdfr		}
82127609Sdfr		if (ndp->ni_pathlen > 1)
822111119Simp			cp = uma_zalloc(namei_zone, M_WAITOK);
8231541Srgrimes		else
82427446Sdfr			cp = cnp->cn_pnbuf;
82527446Sdfr		aiov.iov_base = cp;
82627446Sdfr		aiov.iov_len = MAXPATHLEN;
82727446Sdfr		auio.uio_iov = &aiov;
82827446Sdfr		auio.uio_iovcnt = 1;
82927446Sdfr		auio.uio_offset = 0;
83027446Sdfr		auio.uio_rw = UIO_READ;
83127446Sdfr		auio.uio_segflg = UIO_SYSSPACE;
83299797Sdillon		auio.uio_td = NULL;
83327446Sdfr		auio.uio_resid = MAXPATHLEN;
83427446Sdfr		error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
83527446Sdfr		if (error) {
83648125Sjulian		badlink1:
83727446Sdfr			if (ndp->ni_pathlen > 1)
83892783Sjeff				uma_zfree(namei_zone, cp);
83948125Sjulian		badlink2:
840155160Sjeff			vput(ndp->ni_vp);
84148125Sjulian			vrele(ndp->ni_dvp);
84227446Sdfr			break;
84327446Sdfr		}
84427446Sdfr		linklen = MAXPATHLEN - auio.uio_resid;
84527446Sdfr		if (linklen == 0) {
84627446Sdfr			error = ENOENT;
84748125Sjulian			goto badlink1;
84827446Sdfr		}
84927446Sdfr		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
85027446Sdfr			error = ENAMETOOLONG;
85148125Sjulian			goto badlink1;
85227446Sdfr		}
85348125Sjulian
85448125Sjulian		/*
85548125Sjulian		 * Adjust or replace path
85648125Sjulian		 */
85727446Sdfr		if (ndp->ni_pathlen > 1) {
85827446Sdfr			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
85992783Sjeff			uma_zfree(namei_zone, cnp->cn_pnbuf);
86027446Sdfr			cnp->cn_pnbuf = cp;
86127446Sdfr		} else
86227446Sdfr			cnp->cn_pnbuf[linklen] = '\0';
86327446Sdfr		ndp->ni_pathlen += linklen;
86448125Sjulian
86527446Sdfr		/*
86683651Speter		 * Cleanup refs for next loop and check if root directory
86783651Speter		 * should replace current directory.  Normally ni_dvp
86848125Sjulian		 * becomes the new base directory and is cleaned up when
86948125Sjulian		 * we loop.  Explicitly null pointers after invalidation
87048125Sjulian		 * to clarify operation.
87127446Sdfr		 */
87248125Sjulian		vput(ndp->ni_vp);
87348125Sjulian		ndp->ni_vp = NULL;
87448125Sjulian
87527446Sdfr		if (cnp->cn_pnbuf[0] == '/') {
87648125Sjulian			vrele(ndp->ni_dvp);
87748125Sjulian			ndp->ni_dvp = ndp->ni_rootdir;
87848125Sjulian			VREF(ndp->ni_dvp);
87927446Sdfr		}
88048125Sjulian		ndp->ni_startdir = ndp->ni_dvp;
88148125Sjulian		ndp->ni_dvp = NULL;
8821541Srgrimes	}
883115301Struckman	if (!lockleaf)
884115301Struckman		cnp->cn_flags &= ~LOCKLEAF;
885159268Skib	if (cnp->cn_flags & GIANTHELD) {
886159268Skib		mtx_unlock(&Giant);
887159268Skib		cnp->cn_flags &= ~GIANTHELD;
888159268Skib	}
88948125Sjulian
89048125Sjulian	/*
89148125Sjulian	 * nfs_namei() guarentees that fields will not contain garbage
89248125Sjulian	 * whether an error occurs or not.  This allows the caller to track
89348125Sjulian	 * cleanup state trivially.
89448125Sjulian	 */
8951541Srgrimesout:
89648125Sjulian	if (error) {
89792783Sjeff		uma_zfree(namei_zone, cnp->cn_pnbuf);
89848125Sjulian		ndp->ni_vp = NULL;
89948125Sjulian		ndp->ni_dvp = NULL;
90048125Sjulian		ndp->ni_startdir = NULL;
90148125Sjulian		cnp->cn_flags &= ~HASBUF;
902167665Sjeff		VFS_UNLOCK_GIANT(vfslocked);
903167665Sjeff		vfslocked = 0;
90448125Sjulian	} else if ((ndp->ni_cnd.cn_flags & (WANTPARENT|LOCKPARENT)) == 0) {
90548125Sjulian		ndp->ni_dvp = NULL;
90648125Sjulian	}
907167665Sjeff	/*
908167665Sjeff	 * This differs from normal namei() in that even on failure we may
909167665Sjeff	 * return with Giant held due to the dirp return.  Make sure we only
910167665Sjeff	 * have not recursed however.  The calling code only expects to drop
911167665Sjeff	 * one acquire.
912167665Sjeff	 */
913167665Sjeff	if (vfslocked || dvfslocked)
914167665Sjeff		ndp->ni_cnd.cn_flags |= GIANTHELD;
915167665Sjeff	if (vfslocked && dvfslocked)
916167665Sjeff		VFS_UNLOCK_GIANT(vfslocked);
9171541Srgrimes	return (error);
9181541Srgrimes}
9191541Srgrimes
9201541Srgrimes/*
9211541Srgrimes * A fiddled version of m_adj() that ensures null fill to a long
9221541Srgrimes * boundary and only trims off the back end
9231541Srgrimes */
9241541Srgrimesvoid
92583651Speternfsm_adj(struct mbuf *mp, int len, int nul)
9261541Srgrimes{
92783651Speter	struct mbuf *m;
92883651Speter	int count, i;
92983651Speter	char *cp;
9301541Srgrimes
9311541Srgrimes	/*
9321541Srgrimes	 * Trim from tail.  Scan the mbuf chain,
9331541Srgrimes	 * calculating its length and finding the last mbuf.
9341541Srgrimes	 * If the adjustment only affects this mbuf, then just
9351541Srgrimes	 * adjust and return.  Otherwise, rescan and truncate
9361541Srgrimes	 * after the remaining size.
9371541Srgrimes	 */
9381541Srgrimes	count = 0;
9391541Srgrimes	m = mp;
9401541Srgrimes	for (;;) {
9411541Srgrimes		count += m->m_len;
94299797Sdillon		if (m->m_next == NULL)
9431541Srgrimes			break;
9441541Srgrimes		m = m->m_next;
9451541Srgrimes	}
9461541Srgrimes	if (m->m_len > len) {
9471541Srgrimes		m->m_len -= len;
9481541Srgrimes		if (nul > 0) {
9491541Srgrimes			cp = mtod(m, caddr_t)+m->m_len-nul;
9501541Srgrimes			for (i = 0; i < nul; i++)
9511541Srgrimes				*cp++ = '\0';
9521541Srgrimes		}
9531541Srgrimes		return;
9541541Srgrimes	}
9551541Srgrimes	count -= len;
9561541Srgrimes	if (count < 0)
9571541Srgrimes		count = 0;
9581541Srgrimes	/*
9591541Srgrimes	 * Correct length for chain is "count".
9601541Srgrimes	 * Find the mbuf with last data, adjust its length,
9611541Srgrimes	 * and toss data from remaining mbufs on chain.
9621541Srgrimes	 */
9631541Srgrimes	for (m = mp; m; m = m->m_next) {
9641541Srgrimes		if (m->m_len >= count) {
9651541Srgrimes			m->m_len = count;
9661541Srgrimes			if (nul > 0) {
9671541Srgrimes				cp = mtod(m, caddr_t)+m->m_len-nul;
9681541Srgrimes				for (i = 0; i < nul; i++)
9691541Srgrimes					*cp++ = '\0';
9701541Srgrimes			}
971144246Ssam			if (m->m_next != NULL) {
972144246Ssam				m_freem(m->m_next);
973144246Ssam				m->m_next = NULL;
974144246Ssam			}
9751541Srgrimes			break;
9761541Srgrimes		}
9771541Srgrimes		count -= m->m_len;
9781541Srgrimes	}
9791541Srgrimes}
9801541Srgrimes
9811541Srgrimes/*
9829336Sdfr * Make these functions instead of macros, so that the kernel text size
9839336Sdfr * doesn't get too big...
9849336Sdfr */
9859336Sdfrvoid
98683651Speternfsm_srvwcc(struct nfsrv_descript *nfsd, int before_ret,
98783651Speter    struct vattr *before_vap, int after_ret, struct vattr *after_vap,
98883651Speter    struct mbuf **mbp, char **bposp)
9899336Sdfr{
99083651Speter	struct mbuf *mb = *mbp;
99183651Speter	char *bpos = *bposp;
99283651Speter	u_int32_t *tl;
9939336Sdfr
9949336Sdfr	if (before_ret) {
99584002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
99689094Smsmith		*tl = nfsrv_nfs_false;
9979336Sdfr	} else {
99884002Speter		tl = nfsm_build(u_int32_t *, 7 * NFSX_UNSIGNED);
99989094Smsmith		*tl++ = nfsrv_nfs_true;
100047751Speter		txdr_hyper(before_vap->va_size, tl);
10019336Sdfr		tl += 2;
10029336Sdfr		txdr_nfsv3time(&(before_vap->va_mtime), tl);
10039336Sdfr		tl += 2;
10049336Sdfr		txdr_nfsv3time(&(before_vap->va_ctime), tl);
10059336Sdfr	}
10069336Sdfr	*bposp = bpos;
10079336Sdfr	*mbp = mb;
10089336Sdfr	nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp);
10099336Sdfr}
10109336Sdfr
10119336Sdfrvoid
101283651Speternfsm_srvpostopattr(struct nfsrv_descript *nfsd, int after_ret,
101383651Speter    struct vattr *after_vap, struct mbuf **mbp, char **bposp)
10149336Sdfr{
101583651Speter	struct mbuf *mb = *mbp;
101683651Speter	char *bpos = *bposp;
101783651Speter	u_int32_t *tl;
101883651Speter	struct nfs_fattr *fp;
10199336Sdfr
10209336Sdfr	if (after_ret) {
102184002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
102289094Smsmith		*tl = nfsrv_nfs_false;
10239336Sdfr	} else {
102484002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED + NFSX_V3FATTR);
102589094Smsmith		*tl++ = nfsrv_nfs_true;
10269336Sdfr		fp = (struct nfs_fattr *)tl;
10279336Sdfr		nfsm_srvfattr(nfsd, after_vap, fp);
10289336Sdfr	}
10299336Sdfr	*mbp = mb;
10309336Sdfr	*bposp = bpos;
10319336Sdfr}
10329336Sdfr
10339336Sdfrvoid
103483651Speternfsm_srvfattr(struct nfsrv_descript *nfsd, struct vattr *vap,
103583651Speter    struct nfs_fattr *fp)
10369336Sdfr{
10379336Sdfr
10389336Sdfr	fp->fa_nlink = txdr_unsigned(vap->va_nlink);
10399336Sdfr	fp->fa_uid = txdr_unsigned(vap->va_uid);
10409336Sdfr	fp->fa_gid = txdr_unsigned(vap->va_gid);
10419336Sdfr	if (nfsd->nd_flag & ND_NFSV3) {
10429336Sdfr		fp->fa_type = vtonfsv3_type(vap->va_type);
10439336Sdfr		fp->fa_mode = vtonfsv3_mode(vap->va_mode);
104447751Speter		txdr_hyper(vap->va_size, &fp->fa3_size);
104547751Speter		txdr_hyper(vap->va_bytes, &fp->fa3_used);
104647028Sphk		fp->fa3_rdev.specdata1 = txdr_unsigned(umajor(vap->va_rdev));
104747028Sphk		fp->fa3_rdev.specdata2 = txdr_unsigned(uminor(vap->va_rdev));
10489336Sdfr		fp->fa3_fsid.nfsuquad[0] = 0;
10499336Sdfr		fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
10509336Sdfr		fp->fa3_fileid.nfsuquad[0] = 0;
10519336Sdfr		fp->fa3_fileid.nfsuquad[1] = txdr_unsigned(vap->va_fileid);
10529336Sdfr		txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
10539336Sdfr		txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
10549336Sdfr		txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
10559336Sdfr	} else {
10569336Sdfr		fp->fa_type = vtonfsv2_type(vap->va_type);
10579336Sdfr		fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
10589336Sdfr		fp->fa2_size = txdr_unsigned(vap->va_size);
10599336Sdfr		fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
10609336Sdfr		if (vap->va_type == VFIFO)
10619336Sdfr			fp->fa2_rdev = 0xffffffff;
10629336Sdfr		else
10639336Sdfr			fp->fa2_rdev = txdr_unsigned(vap->va_rdev);
10649336Sdfr		fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
10659336Sdfr		fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
10669336Sdfr		fp->fa2_fileid = txdr_unsigned(vap->va_fileid);
10679336Sdfr		txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
10689336Sdfr		txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
10699336Sdfr		txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
10709336Sdfr	}
10719336Sdfr}
10729336Sdfr
10739336Sdfr/*
10741541Srgrimes * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
10751541Srgrimes * 	- look up fsid in mount list (if not found ret error)
10761541Srgrimes *	- get vp and export rights by calling VFS_FHTOVP()
10771541Srgrimes *	- if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
10781541Srgrimes *	- if not lockflag unlock it with VOP_UNLOCK()
10791541Srgrimes */
10801549Srgrimesint
1081167665Sjeffnfsrv_fhtovp(fhandle_t *fhp, int lockflag, struct vnode **vpp, int *vfslockedp,
108283651Speter    struct ucred *cred, struct nfssvc_sock *slp, struct sockaddr *nam,
108383651Speter    int *rdonlyp, int pubflag)
10841541Srgrimes{
108583651Speter	struct mount *mp;
108683651Speter	int i;
10871541Srgrimes	struct ucred *credanon;
10881541Srgrimes	int error, exflags;
108936534Speter#ifdef MNT_EXNORESPORT		/* XXX needs mountd and /etc/exports help yet */
109036534Speter	struct sockaddr_int *saddr;
109136534Speter#endif
1092167665Sjeff	int vfslocked;
10931541Srgrimes
1094167665Sjeff	*vfslockedp = 0;
109599797Sdillon	*vpp = NULL;
109627446Sdfr
109727446Sdfr	if (nfs_ispublicfh(fhp)) {
109827446Sdfr		if (!pubflag || !nfs_pub.np_valid)
109927446Sdfr			return (ESTALE);
110027446Sdfr		fhp = &nfs_pub.np_handle;
110127446Sdfr	}
110227446Sdfr
110322521Sdyson	mp = vfs_getvfs(&fhp->fh_fsid);
11043305Sphk	if (!mp)
11051541Srgrimes		return (ESTALE);
1106157325Sjeff	vfslocked = VFS_LOCK_GIANT(mp);
110751138Salfred	error = VFS_CHECKEXP(mp, nam, &exflags, &credanon);
11083305Sphk	if (error)
1109129639Srwatson		goto out;
111051138Salfred	error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp);
111151138Salfred	if (error)
1112129639Srwatson		goto out;
111336534Speter#ifdef MNT_EXNORESPORT
111436534Speter	if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) {
111536534Speter		saddr = (struct sockaddr_in *)nam;
1116100134Salfred		if ((saddr->sin_family == AF_INET ||
1117100134Salfred		     saddr->sin_family == AF_INET6) &&
1118100134Salfred	/* same code for INET and INET6: sin*_port at same offet */
111936534Speter		    ntohs(saddr->sin_port) >= IPPORT_RESERVED) {
112036534Speter			vput(*vpp);
112154485Sdillon			*vpp = NULL;
1122129639Srwatson			error = NFSERR_AUTHERR | AUTH_TOOWEAK;
112336534Speter		}
112436534Speter	}
112536534Speter#endif
11261541Srgrimes	/*
11271541Srgrimes	 * Check/setup credentials.
11281541Srgrimes	 */
112983651Speter	if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
11301541Srgrimes		cred->cr_uid = credanon->cr_uid;
11311541Srgrimes		for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
11321541Srgrimes			cred->cr_groups[i] = credanon->cr_groups[i];
11333664Sphk		cred->cr_ngroups = i;
11341541Srgrimes	}
11351541Srgrimes	if (exflags & MNT_EXRDONLY)
11361541Srgrimes		*rdonlyp = 1;
11371541Srgrimes	else
11381541Srgrimes		*rdonlyp = 0;
11397969Sdyson
11401541Srgrimes	if (!lockflag)
1141175294Sattilio		VOP_UNLOCK(*vpp, 0);
1142129639Srwatsonout:
1143157325Sjeff	vfs_rel(mp);
1144167665Sjeff	if (error) {
1145167665Sjeff		VFS_UNLOCK_GIANT(vfslocked);
1146167665Sjeff	} else
1147167665Sjeff		*vfslockedp = vfslocked;
1148164585Srwatson	return (error);
1149164585Srwatson}
1150164585Srwatson
1151164585Srwatson
115227446Sdfr/*
115327446Sdfr * WebNFS: check if a filehandle is a public filehandle. For v3, this
115427446Sdfr * means a length of 0, for v2 it means all zeroes. nfsm_srvmtofh has
115527446Sdfr * transformed this to all zeroes in both cases, so check for it.
115627446Sdfr */
115727446Sdfrint
115883651Speternfs_ispublicfh(fhandle_t *fhp)
115927446Sdfr{
116027446Sdfr	char *cp = (char *)fhp;
116127446Sdfr	int i;
116227446Sdfr
1163129639Srwatson	NFSD_LOCK_DONTCARE();
1164129639Srwatson
116527446Sdfr	for (i = 0; i < NFSX_V3FH; i++)
116627446Sdfr		if (*cp++ != 0)
116727446Sdfr			return (FALSE);
116827446Sdfr	return (TRUE);
116927446Sdfr}
117083651Speter
11711541Srgrimes/*
11721541Srgrimes * This function compares two net addresses by family and returns TRUE
11731541Srgrimes * if they are the same host.
11741541Srgrimes * If there is any doubt, return FALSE.
11751541Srgrimes * The AF_INET family is handled as a special case so that address mbufs
11761541Srgrimes * don't need to be saved to store "struct in_addr", which is only 4 bytes.
11771541Srgrimes */
11781549Srgrimesint
117983651Speternetaddr_match(int family, union nethostaddr *haddr, struct sockaddr *nam)
11801541Srgrimes{
118183651Speter	struct sockaddr_in *inetaddr;
11821541Srgrimes
1183129639Srwatson	NFSD_LOCK_DONTCARE();
1184129639Srwatson
11851541Srgrimes	switch (family) {
11861541Srgrimes	case AF_INET:
118728270Swollman		inetaddr = (struct sockaddr_in *)nam;
11881541Srgrimes		if (inetaddr->sin_family == AF_INET &&
11891541Srgrimes		    inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
11901541Srgrimes			return (1);
11911541Srgrimes		break;
1192100134Salfred#ifdef INET6
1193100134Salfred	case AF_INET6:
1194100134Salfred	{
1195100134Salfred		register struct sockaddr_in6 *inet6addr1, *inet6addr2;
1196100134Salfred
1197100134Salfred		inet6addr1 = (struct sockaddr_in6 *)nam;
1198100134Salfred		inet6addr2 = (struct sockaddr_in6 *)haddr->had_nam;
1199100134Salfred	/* XXX - should test sin6_scope_id ? */
1200100134Salfred		if (inet6addr1->sin6_family == AF_INET6 &&
1201100134Salfred		    IN6_ARE_ADDR_EQUAL(&inet6addr1->sin6_addr,
1202100134Salfred				       &inet6addr2->sin6_addr))
1203100134Salfred			return (1);
1204100134Salfred		break;
1205100134Salfred	}
1206100134Salfred#endif
12071541Srgrimes	default:
12081541Srgrimes		break;
12091541Srgrimes	};
12101541Srgrimes	return (0);
12111541Srgrimes}
12125455Sdg
12139336Sdfr/*
12149336Sdfr * Map errnos to NFS error numbers. For Version 3 also filter out error
12159336Sdfr * numbers not specified for the associated procedure.
12169336Sdfr */
12175455Sdgint
121883651Speternfsrv_errmap(struct nfsrv_descript *nd, int err)
12199336Sdfr{
1220129639Srwatson	const short *defaulterrp, *errp;
1221102236Sphk	int e;
12229336Sdfr
1223129639Srwatson
12249336Sdfr	if (nd->nd_flag & ND_NFSV3) {
12259336Sdfr	    if (nd->nd_procnum <= NFSPROC_COMMIT) {
12269336Sdfr		errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
12279336Sdfr		while (*++errp) {
12289336Sdfr			if (*errp == err)
12299336Sdfr				return (err);
12309336Sdfr			else if (*errp > err)
12319336Sdfr				break;
12329336Sdfr		}
12339336Sdfr		return ((int)*defaulterrp);
12349336Sdfr	    } else
12359336Sdfr		return (err & 0xffff);
12369336Sdfr	}
1237102236Sphk	e = 0;
12389336Sdfr	if (err <= ELAST)
1239102236Sphk		e = nfsrv_v2errmap[err - 1];
1240102236Sphk	if (e != 0)
1241102236Sphk		return (e);
12429336Sdfr	return (NFSERR_IO);
12439336Sdfr}
12449336Sdfr
124536503Speter/*
124636503Speter * Sort the group list in increasing numerical order.
124736503Speter * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
124836503Speter *  that used to be here.)
124936503Speter */
125036503Spetervoid
125183651Speternfsrvw_sort(gid_t *list, int num)
125236503Speter{
125383651Speter	int i, j;
125436503Speter	gid_t v;
125536503Speter
125636503Speter	/* Insertion sort. */
125736503Speter	for (i = 1; i < num; i++) {
125836503Speter		v = list[i];
125936503Speter		/* find correct slot for value v, moving others up */
126036503Speter		for (j = i; --j >= 0 && v < list[j];)
126136503Speter			list[j + 1] = list[j];
126236503Speter		list[j + 1] = v;
126336503Speter	}
126436503Speter}
126536503Speter
126636503Speter/*
126783651Speter * Helper functions for macros.
126883651Speter */
126983651Speter
127083651Spetervoid
127188091Siedowsenfsm_srvfhtom_xx(fhandle_t *f, int v3, struct mbuf **mb, caddr_t *bpos)
127283651Speter{
127388091Siedowse	u_int32_t *tl;
127483651Speter
127583651Speter	if (v3) {
127688091Siedowse		tl = nfsm_build_xx(NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
127788091Siedowse		*tl++ = txdr_unsigned(NFSX_V3FH);
127888091Siedowse		bcopy(f, tl, NFSX_V3FH);
127983651Speter	} else {
128088091Siedowse		tl = nfsm_build_xx(NFSX_V2FH, mb, bpos);
128188091Siedowse		bcopy(f, tl, NFSX_V2FH);
128283651Speter	}
128383651Speter}
128483651Speter
128583651Spetervoid
128688091Siedowsenfsm_srvpostop_fh_xx(fhandle_t *f, struct mbuf **mb, caddr_t *bpos)
128783651Speter{
128888091Siedowse	u_int32_t *tl;
128984002Speter
129088091Siedowse	tl = nfsm_build_xx(2 * NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
129189094Smsmith	*tl++ = nfsrv_nfs_true;
129288091Siedowse	*tl++ = txdr_unsigned(NFSX_V3FH);
129388091Siedowse	bcopy(f, tl, NFSX_V3FH);
129483651Speter}
129583651Speter
129683651Speterint
129788091Siedowsenfsm_srvstrsiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
129883651Speter{
129988091Siedowse	u_int32_t *tl;
130083651Speter
1301140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
130288091Siedowse	if (tl == NULL)
130384057Speter		return EBADRPC;
130488091Siedowse	*s = fxdr_unsigned(int32_t, *tl);
130583651Speter	if (*s > m || *s <= 0)
130683651Speter		return EBADRPC;
130783651Speter	return 0;
130883651Speter}
130983651Speter
131083651Speterint
1311106264Sjeffnfsm_srvnamesiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
131283651Speter{
131388091Siedowse	u_int32_t *tl;
131483651Speter
1315129639Srwatson	NFSD_LOCK_DONTCARE();
1316129639Srwatson
1317140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
131888091Siedowse	if (tl == NULL)
131984057Speter		return EBADRPC;
132088091Siedowse	*s = fxdr_unsigned(int32_t, *tl);
1321106264Sjeff	if (*s > m)
132283651Speter		return NFSERR_NAMETOL;
132383651Speter	if (*s <= 0)
132483651Speter		return EBADRPC;
132583651Speter	return 0;
132683651Speter}
132783651Speter
1328165739Shrsint
1329165739Shrsnfsm_srvnamesiz0_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
1330165739Shrs{
1331165739Shrs	u_int32_t *tl;
1332165739Shrs
1333165739Shrs	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1334165739Shrs	if (tl == NULL)
1335165739Shrs		return EBADRPC;
1336165739Shrs	*s = fxdr_unsigned(int32_t, *tl);
1337165739Shrs	if (*s > m)
1338165739Shrs		return NFSERR_NAMETOL;
1339165739Shrs	if (*s < 0)
1340165739Shrs		return EBADRPC;
1341165739Shrs	return 0;
1342165739Shrs}
1343165739Shrs
134483651Spetervoid
134583651Speternfsm_clget_xx(u_int32_t **tl, struct mbuf *mb, struct mbuf **mp,
1346167665Sjeff    char **bp, char **be, caddr_t bpos)
134783651Speter{
134883651Speter	struct mbuf *nmp;
134983651Speter
1350167665Sjeff	NFSD_UNLOCK_ASSERT();
1351129639Srwatson
135283651Speter	if (*bp >= *be) {
135383651Speter		if (*mp == mb)
135483651Speter			(*mp)->m_len += *bp - bpos;
1355177599Sru		MGET(nmp, M_WAIT, MT_DATA);
1356177599Sru		MCLGET(nmp, M_WAIT);
135783651Speter		nmp->m_len = NFSMSIZ(nmp);
135883651Speter		(*mp)->m_next = nmp;
135983651Speter		*mp = nmp;
136083651Speter		*bp = mtod(*mp, caddr_t);
136183651Speter		*be = *bp + (*mp)->m_len;
136283651Speter	}
136383651Speter	*tl = (u_int32_t *)*bp;
136483651Speter}
136583651Speter
136683651Speterint
136788091Siedowsenfsm_srvmtofh_xx(fhandle_t *f, struct nfsrv_descript *nfsd, struct mbuf **md,
136888091Siedowse    caddr_t *dpos)
136983651Speter{
137088091Siedowse	u_int32_t *tl;
137183651Speter	int fhlen;
137283651Speter
137383651Speter	if (nfsd->nd_flag & ND_NFSV3) {
1374140495Sps		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
137588091Siedowse		if (tl == NULL)
137684057Speter			return EBADRPC;
137788091Siedowse		fhlen = fxdr_unsigned(int, *tl);
137883651Speter		if (fhlen != 0 && fhlen != NFSX_V3FH)
137983651Speter			return EBADRPC;
138083651Speter	} else {
138183651Speter		fhlen = NFSX_V2FH;
138283651Speter	}
138383651Speter	if (fhlen != 0) {
1384140495Sps		tl = nfsm_dissect_xx_nonblock(fhlen, md, dpos);
138588091Siedowse		if (tl == NULL)
138684057Speter			return EBADRPC;
138788091Siedowse		bcopy((caddr_t)tl, (caddr_t)(f), fhlen);
138883651Speter	} else {
138983651Speter		bzero((caddr_t)(f), NFSX_V3FH);
139083651Speter	}
139183651Speter	return 0;
139283651Speter}
139383651Speter
139483651Speterint
139588091Siedowsenfsm_srvsattr_xx(struct vattr *a, struct mbuf **md, caddr_t *dpos)
139683651Speter{
139788091Siedowse	u_int32_t *tl;
1398157391Scel	int toclient = 0;
139983651Speter
1400140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
140188091Siedowse	if (tl == NULL)
140284057Speter		return EBADRPC;
140389094Smsmith	if (*tl == nfsrv_nfs_true) {
1404140495Sps		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
140588091Siedowse		if (tl == NULL)
140684057Speter			return EBADRPC;
140788091Siedowse		(a)->va_mode = nfstov_mode(*tl);
140883651Speter	}
1409140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
141088091Siedowse	if (tl == NULL)
141184057Speter		return EBADRPC;
141289094Smsmith	if (*tl == nfsrv_nfs_true) {
1413140495Sps		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
141488091Siedowse		if (tl == NULL)
141584057Speter			return EBADRPC;
141688091Siedowse		(a)->va_uid = fxdr_unsigned(uid_t, *tl);
141783651Speter	}
1418140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
141988091Siedowse	if (tl == NULL)
142084057Speter		return EBADRPC;
142189094Smsmith	if (*tl == nfsrv_nfs_true) {
1422140495Sps		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
142388091Siedowse		if (tl == NULL)
142484057Speter			return EBADRPC;
142588091Siedowse		(a)->va_gid = fxdr_unsigned(gid_t, *tl);
142683651Speter	}
1427140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
142888091Siedowse	if (tl == NULL)
142984057Speter		return EBADRPC;
143089094Smsmith	if (*tl == nfsrv_nfs_true) {
1431140495Sps		tl = nfsm_dissect_xx_nonblock(2 * NFSX_UNSIGNED, md, dpos);
143288091Siedowse		if (tl == NULL)
143384057Speter			return EBADRPC;
143488091Siedowse		(a)->va_size = fxdr_hyper(tl);
143583651Speter	}
1436140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
143788091Siedowse	if (tl == NULL)
143884057Speter		return EBADRPC;
143988091Siedowse	switch (fxdr_unsigned(int, *tl)) {
144083651Speter	case NFSV3SATTRTIME_TOCLIENT:
1441140495Sps		tl = nfsm_dissect_xx_nonblock(2 * NFSX_UNSIGNED, md, dpos);
144288091Siedowse		if (tl == NULL)
144384057Speter			return EBADRPC;
144488091Siedowse		fxdr_nfsv3time(tl, &(a)->va_atime);
1445157391Scel		toclient = 1;
144683651Speter		break;
144783651Speter	case NFSV3SATTRTIME_TOSERVER:
144883651Speter		getnanotime(&(a)->va_atime);
1449157391Scel		a->va_vaflags |= VA_UTIMES_NULL;
145083651Speter		break;
145183651Speter	}
1452140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
145388091Siedowse	if (tl == NULL)
145484057Speter		return EBADRPC;
145588091Siedowse	switch (fxdr_unsigned(int, *tl)) {
145683651Speter	case NFSV3SATTRTIME_TOCLIENT:
1457140495Sps		tl = nfsm_dissect_xx_nonblock(2 * NFSX_UNSIGNED, md, dpos);
145888091Siedowse		if (tl == NULL)
145984057Speter			return EBADRPC;
146088091Siedowse		fxdr_nfsv3time(tl, &(a)->va_mtime);
1461157391Scel		a->va_vaflags &= ~VA_UTIMES_NULL;
146283651Speter		break;
146383651Speter	case NFSV3SATTRTIME_TOSERVER:
146483651Speter		getnanotime(&(a)->va_mtime);
1465157391Scel		if (toclient == 0)
1466157391Scel			a->va_vaflags |= VA_UTIMES_NULL;
146783651Speter		break;
146883651Speter	}
146983651Speter	return 0;
147083651Speter}
1471