nfs_srvsubs.c revision 205661
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 205661 2010-03-26 01:19:29Z rmacklem $");
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
70195202Sdfr#include <rpc/rpc.h>
71195202Sdfr
729336Sdfr#include <nfs/nfsproto.h>
7383651Speter#include <nfsserver/nfs.h>
741541Srgrimes#include <nfs/xdr_subs.h>
7583651Speter#include <nfsserver/nfsm_subs.h>
761541Srgrimes
771541Srgrimes#include <netinet/in.h>
781541Srgrimes
791541Srgrimes/*
801541Srgrimes * Data items converted to xdr at startup, since they are constant
811541Srgrimes * This is kinda hokey, but may save a little time doing byte swaps
821541Srgrimes */
8389094Smsmithu_int32_t nfsrv_nfs_xdrneg1;
84195202Sdfru_int32_t nfsrv_nfs_true, nfsrv_nfs_false;
851541Srgrimes
861541Srgrimes/* And other global data */
87129639Srwatsonstatic const nfstype nfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR,
88129639Srwatson				       NFLNK, NFNON, NFCHR, NFNON };
8983651Speter#define vtonfsv2_type(a)	txdr_unsigned(nfsv2_type[((int32_t)(a))])
9083651Speter#define vtonfsv3_mode(m)	txdr_unsigned((m) & ALLPERMS)
9112911Sphk
9289094Smsmithint nfsrv_ticks;
939336Sdfr
94129639Srwatsonstruct mtx nfsd_mtx;
95129639Srwatson
969336Sdfr/*
979336Sdfr * Mapping of old NFS Version 2 RPC numbers to generic numbers.
989336Sdfr */
99129639Srwatsonconst int nfsrv_nfsv3_procid[NFS_NPROCS] = {
1009336Sdfr	NFSPROC_NULL,
1019336Sdfr	NFSPROC_GETATTR,
1029336Sdfr	NFSPROC_SETATTR,
1039336Sdfr	NFSPROC_NOOP,
1049336Sdfr	NFSPROC_LOOKUP,
1059336Sdfr	NFSPROC_READLINK,
1069336Sdfr	NFSPROC_READ,
1079336Sdfr	NFSPROC_NOOP,
1089336Sdfr	NFSPROC_WRITE,
1099336Sdfr	NFSPROC_CREATE,
1109336Sdfr	NFSPROC_REMOVE,
1119336Sdfr	NFSPROC_RENAME,
1129336Sdfr	NFSPROC_LINK,
1139336Sdfr	NFSPROC_SYMLINK,
1149336Sdfr	NFSPROC_MKDIR,
1159336Sdfr	NFSPROC_RMDIR,
1169336Sdfr	NFSPROC_READDIR,
1179336Sdfr	NFSPROC_FSSTAT,
1189336Sdfr	NFSPROC_NOOP,
1199336Sdfr	NFSPROC_NOOP,
1209336Sdfr	NFSPROC_NOOP,
1219336Sdfr	NFSPROC_NOOP,
1229336Sdfr	NFSPROC_NOOP,
1239336Sdfr};
1249336Sdfr
1259336Sdfr/*
1269336Sdfr * and the reverse mapping from generic to Version 2 procedure numbers
1279336Sdfr */
128129639Srwatsonconst int nfsrvv2_procid[NFS_NPROCS] = {
1299336Sdfr	NFSV2PROC_NULL,
1309336Sdfr	NFSV2PROC_GETATTR,
1319336Sdfr	NFSV2PROC_SETATTR,
1329336Sdfr	NFSV2PROC_LOOKUP,
1339336Sdfr	NFSV2PROC_NOOP,
1349336Sdfr	NFSV2PROC_READLINK,
1359336Sdfr	NFSV2PROC_READ,
1369336Sdfr	NFSV2PROC_WRITE,
1379336Sdfr	NFSV2PROC_CREATE,
1389336Sdfr	NFSV2PROC_MKDIR,
1399336Sdfr	NFSV2PROC_SYMLINK,
1409336Sdfr	NFSV2PROC_CREATE,
1419336Sdfr	NFSV2PROC_REMOVE,
1429336Sdfr	NFSV2PROC_RMDIR,
1439336Sdfr	NFSV2PROC_RENAME,
1449336Sdfr	NFSV2PROC_LINK,
1459336Sdfr	NFSV2PROC_READDIR,
1469336Sdfr	NFSV2PROC_NOOP,
1479336Sdfr	NFSV2PROC_STATFS,
1489336Sdfr	NFSV2PROC_NOOP,
1499336Sdfr	NFSV2PROC_NOOP,
1509336Sdfr	NFSV2PROC_NOOP,
1519336Sdfr	NFSV2PROC_NOOP,
1529336Sdfr};
1539336Sdfr
1549336Sdfr/*
1559336Sdfr * Maps errno values to nfs error numbers.
156102236Sphk * Use 0 (which gets converted to NFSERR_IO) as the catch all for ones not
157102236Sphk * specifically defined in RFC 1094.
1589336Sdfr */
159129639Srwatsonstatic const u_char nfsrv_v2errmap[ELAST] = {
160102236Sphk  NFSERR_PERM,	NFSERR_NOENT,	0,		0,		0,
161102236Sphk  NFSERR_NXIO,	0,		0,		0,		0,
162102236Sphk  0,		0,		NFSERR_ACCES,	0,		0,
163102236Sphk  0,		NFSERR_EXIST,	0,		NFSERR_NODEV,	NFSERR_NOTDIR,
164102236Sphk  NFSERR_ISDIR,	0,		0,		0,		0,
165102236Sphk  0,		NFSERR_FBIG,	NFSERR_NOSPC,	0,		NFSERR_ROFS,
166102236Sphk  0,		0,		0,		0,		0,
167102236Sphk  0,		0,		0,		0,		0,
168102236Sphk  0,		0,		0,		0,		0,
169102236Sphk  0,		0,		0,		0,		0,
170102236Sphk  0,		0,		0,		0,		0,
171102236Sphk  0,		0,		0,		0,		0,
172102236Sphk  0,		0,		NFSERR_NAMETOL,	0,		0,
173102236Sphk  NFSERR_NOTEMPTY, 0,		0,		NFSERR_DQUOT,	NFSERR_STALE,
174102236Sphk  0
1759336Sdfr};
1769336Sdfr
1779336Sdfr/*
1789336Sdfr * Maps errno values to nfs error numbers.
1799336Sdfr * Although it is not obvious whether or not NFS clients really care if
1809336Sdfr * a returned error value is in the specified list for the procedure, the
1819336Sdfr * safest thing to do is filter them appropriately. For Version 2, the
1829336Sdfr * X/Open XNFS document is the only specification that defines error values
1839336Sdfr * for each RPC (The RFC simply lists all possible error values for all RPCs),
1849336Sdfr * so I have decided to not do this for Version 2.
1859336Sdfr * The first entry is the default error return and the rest are the valid
1869336Sdfr * errors for that RPC in increasing numeric order.
1879336Sdfr */
188129639Srwatsonstatic const short nfsv3err_null[] = {
1899336Sdfr	0,
1909336Sdfr	0,
1919336Sdfr};
1929336Sdfr
193129639Srwatsonstatic const short nfsv3err_getattr[] = {
1949336Sdfr	NFSERR_IO,
1959336Sdfr	NFSERR_IO,
1969336Sdfr	NFSERR_STALE,
1979336Sdfr	NFSERR_BADHANDLE,
1989336Sdfr	NFSERR_SERVERFAULT,
1999336Sdfr	0,
2009336Sdfr};
2019336Sdfr
202129639Srwatsonstatic const short nfsv3err_setattr[] = {
2039336Sdfr	NFSERR_IO,
2049336Sdfr	NFSERR_PERM,
2059336Sdfr	NFSERR_IO,
2069336Sdfr	NFSERR_ACCES,
2079336Sdfr	NFSERR_INVAL,
2089336Sdfr	NFSERR_NOSPC,
2099336Sdfr	NFSERR_ROFS,
2109336Sdfr	NFSERR_DQUOT,
2119336Sdfr	NFSERR_STALE,
2129336Sdfr	NFSERR_BADHANDLE,
2139336Sdfr	NFSERR_NOT_SYNC,
2149336Sdfr	NFSERR_SERVERFAULT,
2159336Sdfr	0,
2169336Sdfr};
2179336Sdfr
218129639Srwatsonstatic const short nfsv3err_lookup[] = {
2199336Sdfr	NFSERR_IO,
2209336Sdfr	NFSERR_NOENT,
2219336Sdfr	NFSERR_IO,
2229336Sdfr	NFSERR_ACCES,
2239336Sdfr	NFSERR_NOTDIR,
2249336Sdfr	NFSERR_NAMETOL,
2259336Sdfr	NFSERR_STALE,
2269336Sdfr	NFSERR_BADHANDLE,
2279336Sdfr	NFSERR_SERVERFAULT,
2289336Sdfr	0,
2299336Sdfr};
2309336Sdfr
231129639Srwatsonstatic const short nfsv3err_access[] = {
2329336Sdfr	NFSERR_IO,
2339336Sdfr	NFSERR_IO,
2349336Sdfr	NFSERR_STALE,
2359336Sdfr	NFSERR_BADHANDLE,
2369336Sdfr	NFSERR_SERVERFAULT,
2379336Sdfr	0,
2389336Sdfr};
2399336Sdfr
240129639Srwatsonstatic const short nfsv3err_readlink[] = {
2419336Sdfr	NFSERR_IO,
2429336Sdfr	NFSERR_IO,
2439336Sdfr	NFSERR_ACCES,
2449336Sdfr	NFSERR_INVAL,
2459336Sdfr	NFSERR_STALE,
2469336Sdfr	NFSERR_BADHANDLE,
2479336Sdfr	NFSERR_NOTSUPP,
2489336Sdfr	NFSERR_SERVERFAULT,
2499336Sdfr	0,
2509336Sdfr};
2519336Sdfr
252129639Srwatsonstatic const short nfsv3err_read[] = {
2539336Sdfr	NFSERR_IO,
2549336Sdfr	NFSERR_IO,
2559336Sdfr	NFSERR_NXIO,
2569336Sdfr	NFSERR_ACCES,
2579336Sdfr	NFSERR_INVAL,
2589336Sdfr	NFSERR_STALE,
2599336Sdfr	NFSERR_BADHANDLE,
2609336Sdfr	NFSERR_SERVERFAULT,
2619336Sdfr	0,
2629336Sdfr};
2639336Sdfr
264129639Srwatsonstatic const short nfsv3err_write[] = {
2659336Sdfr	NFSERR_IO,
2669336Sdfr	NFSERR_IO,
2679336Sdfr	NFSERR_ACCES,
2689336Sdfr	NFSERR_INVAL,
2699336Sdfr	NFSERR_FBIG,
2709336Sdfr	NFSERR_NOSPC,
2719336Sdfr	NFSERR_ROFS,
2729336Sdfr	NFSERR_DQUOT,
2739336Sdfr	NFSERR_STALE,
2749336Sdfr	NFSERR_BADHANDLE,
2759336Sdfr	NFSERR_SERVERFAULT,
2769336Sdfr	0,
2779336Sdfr};
2789336Sdfr
279129639Srwatsonstatic const short nfsv3err_create[] = {
2809336Sdfr	NFSERR_IO,
2819336Sdfr	NFSERR_IO,
2829336Sdfr	NFSERR_ACCES,
2839336Sdfr	NFSERR_EXIST,
2849336Sdfr	NFSERR_NOTDIR,
2859336Sdfr	NFSERR_NOSPC,
2869336Sdfr	NFSERR_ROFS,
2879336Sdfr	NFSERR_NAMETOL,
2889336Sdfr	NFSERR_DQUOT,
2899336Sdfr	NFSERR_STALE,
2909336Sdfr	NFSERR_BADHANDLE,
2919336Sdfr	NFSERR_NOTSUPP,
2929336Sdfr	NFSERR_SERVERFAULT,
2939336Sdfr	0,
2949336Sdfr};
2959336Sdfr
296129639Srwatsonstatic const short nfsv3err_mkdir[] = {
2979336Sdfr	NFSERR_IO,
2989336Sdfr	NFSERR_IO,
2999336Sdfr	NFSERR_ACCES,
3009336Sdfr	NFSERR_EXIST,
3019336Sdfr	NFSERR_NOTDIR,
3029336Sdfr	NFSERR_NOSPC,
3039336Sdfr	NFSERR_ROFS,
3049336Sdfr	NFSERR_NAMETOL,
3059336Sdfr	NFSERR_DQUOT,
3069336Sdfr	NFSERR_STALE,
3079336Sdfr	NFSERR_BADHANDLE,
3089336Sdfr	NFSERR_NOTSUPP,
3099336Sdfr	NFSERR_SERVERFAULT,
3109336Sdfr	0,
3119336Sdfr};
3129336Sdfr
313129639Srwatsonstatic const short nfsv3err_symlink[] = {
3149336Sdfr	NFSERR_IO,
3159336Sdfr	NFSERR_IO,
3169336Sdfr	NFSERR_ACCES,
3179336Sdfr	NFSERR_EXIST,
3189336Sdfr	NFSERR_NOTDIR,
3199336Sdfr	NFSERR_NOSPC,
3209336Sdfr	NFSERR_ROFS,
3219336Sdfr	NFSERR_NAMETOL,
3229336Sdfr	NFSERR_DQUOT,
3239336Sdfr	NFSERR_STALE,
3249336Sdfr	NFSERR_BADHANDLE,
3259336Sdfr	NFSERR_NOTSUPP,
3269336Sdfr	NFSERR_SERVERFAULT,
3279336Sdfr	0,
3289336Sdfr};
3299336Sdfr
330129639Srwatsonstatic const short nfsv3err_mknod[] = {
3319336Sdfr	NFSERR_IO,
3329336Sdfr	NFSERR_IO,
3339336Sdfr	NFSERR_ACCES,
3349336Sdfr	NFSERR_EXIST,
3359336Sdfr	NFSERR_NOTDIR,
3369336Sdfr	NFSERR_NOSPC,
3379336Sdfr	NFSERR_ROFS,
3389336Sdfr	NFSERR_NAMETOL,
3399336Sdfr	NFSERR_DQUOT,
3409336Sdfr	NFSERR_STALE,
3419336Sdfr	NFSERR_BADHANDLE,
3429336Sdfr	NFSERR_NOTSUPP,
3439336Sdfr	NFSERR_SERVERFAULT,
3449336Sdfr	NFSERR_BADTYPE,
3459336Sdfr	0,
3469336Sdfr};
3479336Sdfr
348129639Srwatsonstatic const short nfsv3err_remove[] = {
3499336Sdfr	NFSERR_IO,
3509336Sdfr	NFSERR_NOENT,
3519336Sdfr	NFSERR_IO,
3529336Sdfr	NFSERR_ACCES,
3539336Sdfr	NFSERR_NOTDIR,
3549336Sdfr	NFSERR_ROFS,
3559336Sdfr	NFSERR_NAMETOL,
3569336Sdfr	NFSERR_STALE,
3579336Sdfr	NFSERR_BADHANDLE,
3589336Sdfr	NFSERR_SERVERFAULT,
3599336Sdfr	0,
3609336Sdfr};
3619336Sdfr
362129639Srwatsonstatic const short nfsv3err_rmdir[] = {
3639336Sdfr	NFSERR_IO,
3649336Sdfr	NFSERR_NOENT,
3659336Sdfr	NFSERR_IO,
3669336Sdfr	NFSERR_ACCES,
3679336Sdfr	NFSERR_EXIST,
3689336Sdfr	NFSERR_NOTDIR,
3699336Sdfr	NFSERR_INVAL,
3709336Sdfr	NFSERR_ROFS,
3719336Sdfr	NFSERR_NAMETOL,
3729336Sdfr	NFSERR_NOTEMPTY,
3739336Sdfr	NFSERR_STALE,
3749336Sdfr	NFSERR_BADHANDLE,
3759336Sdfr	NFSERR_NOTSUPP,
3769336Sdfr	NFSERR_SERVERFAULT,
3779336Sdfr	0,
3789336Sdfr};
3799336Sdfr
380129639Srwatsonstatic const short nfsv3err_rename[] = {
3819336Sdfr	NFSERR_IO,
3829336Sdfr	NFSERR_NOENT,
3839336Sdfr	NFSERR_IO,
3849336Sdfr	NFSERR_ACCES,
3859336Sdfr	NFSERR_EXIST,
3869336Sdfr	NFSERR_XDEV,
3879336Sdfr	NFSERR_NOTDIR,
3889336Sdfr	NFSERR_ISDIR,
3899336Sdfr	NFSERR_INVAL,
3909336Sdfr	NFSERR_NOSPC,
3919336Sdfr	NFSERR_ROFS,
3929336Sdfr	NFSERR_MLINK,
3939336Sdfr	NFSERR_NAMETOL,
3949336Sdfr	NFSERR_NOTEMPTY,
3959336Sdfr	NFSERR_DQUOT,
3969336Sdfr	NFSERR_STALE,
3979336Sdfr	NFSERR_BADHANDLE,
3989336Sdfr	NFSERR_NOTSUPP,
3999336Sdfr	NFSERR_SERVERFAULT,
4009336Sdfr	0,
4019336Sdfr};
4029336Sdfr
403129639Srwatsonstatic const short nfsv3err_link[] = {
4049336Sdfr	NFSERR_IO,
4059336Sdfr	NFSERR_IO,
4069336Sdfr	NFSERR_ACCES,
4079336Sdfr	NFSERR_EXIST,
4089336Sdfr	NFSERR_XDEV,
4099336Sdfr	NFSERR_NOTDIR,
4109336Sdfr	NFSERR_INVAL,
4119336Sdfr	NFSERR_NOSPC,
4129336Sdfr	NFSERR_ROFS,
4139336Sdfr	NFSERR_MLINK,
4149336Sdfr	NFSERR_NAMETOL,
4159336Sdfr	NFSERR_DQUOT,
4169336Sdfr	NFSERR_STALE,
4179336Sdfr	NFSERR_BADHANDLE,
4189336Sdfr	NFSERR_NOTSUPP,
4199336Sdfr	NFSERR_SERVERFAULT,
4209336Sdfr	0,
4219336Sdfr};
4229336Sdfr
423129639Srwatsonstatic const short nfsv3err_readdir[] = {
4249336Sdfr	NFSERR_IO,
4259336Sdfr	NFSERR_IO,
4269336Sdfr	NFSERR_ACCES,
4279336Sdfr	NFSERR_NOTDIR,
4289336Sdfr	NFSERR_STALE,
4299336Sdfr	NFSERR_BADHANDLE,
4309336Sdfr	NFSERR_BAD_COOKIE,
4319336Sdfr	NFSERR_TOOSMALL,
4329336Sdfr	NFSERR_SERVERFAULT,
4339336Sdfr	0,
4349336Sdfr};
4359336Sdfr
436129639Srwatsonstatic const short nfsv3err_readdirplus[] = {
4379336Sdfr	NFSERR_IO,
4389336Sdfr	NFSERR_IO,
4399336Sdfr	NFSERR_ACCES,
4409336Sdfr	NFSERR_NOTDIR,
4419336Sdfr	NFSERR_STALE,
4429336Sdfr	NFSERR_BADHANDLE,
4439336Sdfr	NFSERR_BAD_COOKIE,
4449336Sdfr	NFSERR_NOTSUPP,
4459336Sdfr	NFSERR_TOOSMALL,
4469336Sdfr	NFSERR_SERVERFAULT,
4479336Sdfr	0,
4489336Sdfr};
4499336Sdfr
450129639Srwatsonstatic const short nfsv3err_fsstat[] = {
4519336Sdfr	NFSERR_IO,
4529336Sdfr	NFSERR_IO,
4539336Sdfr	NFSERR_STALE,
4549336Sdfr	NFSERR_BADHANDLE,
4559336Sdfr	NFSERR_SERVERFAULT,
4569336Sdfr	0,
4579336Sdfr};
4589336Sdfr
459129639Srwatsonstatic const short nfsv3err_fsinfo[] = {
4609336Sdfr	NFSERR_STALE,
4619336Sdfr	NFSERR_STALE,
4629336Sdfr	NFSERR_BADHANDLE,
4639336Sdfr	NFSERR_SERVERFAULT,
4649336Sdfr	0,
4659336Sdfr};
4669336Sdfr
467129639Srwatsonstatic const short nfsv3err_pathconf[] = {
4689336Sdfr	NFSERR_STALE,
4699336Sdfr	NFSERR_STALE,
4709336Sdfr	NFSERR_BADHANDLE,
4719336Sdfr	NFSERR_SERVERFAULT,
4729336Sdfr	0,
4739336Sdfr};
4749336Sdfr
475129639Srwatsonstatic const short nfsv3err_commit[] = {
4769336Sdfr	NFSERR_IO,
4779336Sdfr	NFSERR_IO,
4789336Sdfr	NFSERR_STALE,
4799336Sdfr	NFSERR_BADHANDLE,
4809336Sdfr	NFSERR_SERVERFAULT,
4819336Sdfr	0,
4829336Sdfr};
4839336Sdfr
484129639Srwatsonstatic const short *nfsrv_v3errmap[] = {
4859336Sdfr	nfsv3err_null,
4869336Sdfr	nfsv3err_getattr,
4879336Sdfr	nfsv3err_setattr,
4889336Sdfr	nfsv3err_lookup,
4899336Sdfr	nfsv3err_access,
4909336Sdfr	nfsv3err_readlink,
4919336Sdfr	nfsv3err_read,
4929336Sdfr	nfsv3err_write,
4939336Sdfr	nfsv3err_create,
4949336Sdfr	nfsv3err_mkdir,
4959336Sdfr	nfsv3err_symlink,
4969336Sdfr	nfsv3err_mknod,
4979336Sdfr	nfsv3err_remove,
4989336Sdfr	nfsv3err_rmdir,
4999336Sdfr	nfsv3err_rename,
5009336Sdfr	nfsv3err_link,
5019336Sdfr	nfsv3err_readdir,
5029336Sdfr	nfsv3err_readdirplus,
5039336Sdfr	nfsv3err_fsstat,
5049336Sdfr	nfsv3err_fsinfo,
5059336Sdfr	nfsv3err_pathconf,
5069336Sdfr	nfsv3err_commit,
5079336Sdfr};
5089336Sdfr
509190971Srmacklemextern int (*nfsd_call_nfsserver)(struct thread *, struct nfssvc_args *);
510190971Srmacklem
5111541Srgrimes/*
5121541Srgrimes * Called once to initialize data structures...
5131541Srgrimes */
51483651Speterstatic int
51583700Speternfsrv_modevent(module_t mod, int type, void *data)
5161541Srgrimes{
517132199Sphk	int error = 0;
5181541Srgrimes
51983700Speter	switch (type) {
52083700Speter	case MOD_LOAD:
521129639Srwatson		mtx_init(&nfsd_mtx, "nfsd_mtx", NULL, MTX_DEF);
52289094Smsmith		nfsrv_nfs_true = txdr_unsigned(TRUE);
52389094Smsmith		nfsrv_nfs_false = txdr_unsigned(FALSE);
52489094Smsmith		nfsrv_nfs_xdrneg1 = txdr_unsigned(-1);
52589094Smsmith		nfsrv_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
52689094Smsmith		if (nfsrv_ticks < 1)
52789094Smsmith			nfsrv_ticks = 1;
52883651Speter
529129639Srwatson		NFSD_LOCK();
53083700Speter		nfsrv_init(0);		/* Init server data structures */
531129639Srwatson		NFSD_UNLOCK();
5321541Srgrimes
533190971Srmacklem		nfsd_call_nfsserver = nfssvc_nfsserver;
53483700Speter		break;
5352997Swollman
536132199Sphk	case MOD_UNLOAD:
537129902Sbmilekic		if (nfsrv_numnfsd != 0) {
538132199Sphk			error = EBUSY;
539132199Sphk			break;
540129902Sbmilekic		}
54183700Speter
542190971Srmacklem		nfsd_call_nfsserver = NULL;
543160881Sjhb		callout_drain(&nfsrv_callout);
544129639Srwatson		mtx_destroy(&nfsd_mtx);
54583700Speter		break;
546132199Sphk	default:
547132199Sphk		error = EOPNOTSUPP;
548132199Sphk		break;
54983700Speter	}
550132199Sphk	return error;
5511541Srgrimes}
55283700Speterstatic moduledata_t nfsserver_mod = {
55383700Speter	"nfsserver",
55483700Speter	nfsrv_modevent,
55583700Speter	NULL,
55683700Speter};
55783700SpeterDECLARE_MODULE(nfsserver, nfsserver_mod, SI_SUB_VFS, SI_ORDER_ANY);
5581541Srgrimes
55983700Speter/* So that loader and kldload(2) can find us, wherever we are.. */
56083700SpeterMODULE_VERSION(nfsserver, 1);
561190971SrmacklemMODULE_DEPEND(nfsserver, nfssvc, 1, 1, 1);
562184716SdesMODULE_DEPEND(nfsserver, krpc, 1, 1, 1);
563203968SmariusMODULE_DEPEND(nfsserver, nfs_common, 1, 1, 1);
56438894Sbde
5651541Srgrimes/*
56627446Sdfr * Set up nameidata for a lookup() call and do it.
56727446Sdfr *
56827446Sdfr * If pubflag is set, this call is done for a lookup operation on the
56927446Sdfr * public filehandle. In that case we allow crossing mountpoints and
57027446Sdfr * absolute pathnames. However, the caller is expected to check that
57127446Sdfr * the lookup result is within the public fs, and deny access if
57227446Sdfr * it is not.
57348125Sjulian *
57448125Sjulian * nfs_namei() clears out garbage fields that namei() might leave garbage.
57548125Sjulian * This is mainly ni_vp and ni_dvp when an error occurs, and ni_dvp when no
57648125Sjulian * error occurs but the parent was not requested.
57748125Sjulian *
57883651Speter * dirp may be set whether an error is returned or not, and must be
57948125Sjulian * released by the caller.
5801541Srgrimes */
5811549Srgrimesint
582184588Sdfrnfs_namei(struct nameidata *ndp, struct nfsrv_descript *nfsd,
583184588Sdfr    fhandle_t *fhp, int len, struct nfssvc_sock *slp,
584184588Sdfr    struct sockaddr *nam, struct mbuf **mdp,
585115301Struckman    caddr_t *dposp, struct vnode **retdirp, int v3, struct vattr *retdirattrp,
586183103Sattilio    int *retdirattr_retp, int pubflag)
5871541Srgrimes{
58883651Speter	int i, rem;
58983651Speter	struct mbuf *md;
59083651Speter	char *fromcp, *tocp, *cp;
59127446Sdfr	struct iovec aiov;
59227446Sdfr	struct uio auio;
5931541Srgrimes	struct vnode *dp;
59427446Sdfr	int error, rdonly, linklen;
5951541Srgrimes	struct componentname *cnp = &ndp->ni_cnd;
596115301Struckman	int lockleaf = (cnp->cn_flags & LOCKLEAF) != 0;
597167665Sjeff	int dvfslocked;
598167665Sjeff	int vfslocked;
5991541Srgrimes
600167665Sjeff	vfslocked = 0;
601167665Sjeff	dvfslocked = 0;
60299797Sdillon	*retdirp = NULL;
603105481Srwatson	cnp->cn_flags |= NOMACCHECK;
604111119Simp	cnp->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
60529653Sdyson
6061541Srgrimes	/*
6071541Srgrimes	 * Copy the name from the mbuf list to ndp->ni_pnbuf
6081541Srgrimes	 * and set the various ndp fields appropriately.
6091541Srgrimes	 */
6101541Srgrimes	fromcp = *dposp;
6111541Srgrimes	tocp = cnp->cn_pnbuf;
6121541Srgrimes	md = *mdp;
6131541Srgrimes	rem = mtod(md, caddr_t) + md->m_len - fromcp;
6141541Srgrimes	for (i = 0; i < len; i++) {
6151541Srgrimes		while (rem == 0) {
6161541Srgrimes			md = md->m_next;
6171541Srgrimes			if (md == NULL) {
6181541Srgrimes				error = EBADRPC;
619167665Sjeff				goto out;
6201541Srgrimes			}
6211541Srgrimes			fromcp = mtod(md, caddr_t);
6221541Srgrimes			rem = md->m_len;
6231541Srgrimes		}
62427446Sdfr		if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) {
6259336Sdfr			error = EACCES;
626167665Sjeff			goto out;
6271541Srgrimes		}
6281541Srgrimes		*tocp++ = *fromcp++;
6291541Srgrimes		rem--;
6301541Srgrimes	}
6311541Srgrimes	*tocp = '\0';
6321541Srgrimes	*mdp = md;
6331541Srgrimes	*dposp = fromcp;
6341541Srgrimes	len = nfsm_rndup(len)-len;
6351541Srgrimes	if (len > 0) {
6361541Srgrimes		if (rem >= len)
6371541Srgrimes			*dposp += len;
63827609Sdfr		else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
639167665Sjeff			goto out;
6401541Srgrimes	}
64127446Sdfr
6421541Srgrimes	/*
6431541Srgrimes	 * Extract and set starting directory.
6441541Srgrimes	 */
645167665Sjeff	error = nfsrv_fhtovp(fhp, FALSE, &dp, &dvfslocked,
646184588Sdfr	    nfsd, slp, nam, &rdonly, pubflag);
64727446Sdfr	if (error)
6481541Srgrimes		goto out;
649167665Sjeff	vfslocked = VFS_LOCK_GIANT(dp->v_mount);
6501541Srgrimes	if (dp->v_type != VDIR) {
65117761Sdyson		vrele(dp);
6521541Srgrimes		error = ENOTDIR;
6531541Srgrimes		goto out;
6541541Srgrimes	}
65527446Sdfr
65627446Sdfr	if (rdonly)
65727446Sdfr		cnp->cn_flags |= RDONLY;
65827446Sdfr
65948125Sjulian	/*
66083651Speter	 * Set return directory.  Reference to dp is implicitly transfered
66148125Sjulian	 * to the returned pointer
66248125Sjulian	 */
66327609Sdfr	*retdirp = dp;
664115301Struckman	if (v3) {
665175202Sattilio		vn_lock(dp, LK_EXCLUSIVE | LK_RETRY);
666115301Struckman		*retdirattr_retp = VOP_GETATTR(dp, retdirattrp,
667182371Sattilio			ndp->ni_cnd.cn_cred);
668175294Sattilio		VOP_UNLOCK(dp, 0);
669115301Struckman	}
67027609Sdfr
67127446Sdfr	if (pubflag) {
67227446Sdfr		/*
67327446Sdfr		 * Oh joy. For WebNFS, handle those pesky '%' escapes,
67427446Sdfr		 * and the 'native path' indicator.
67527446Sdfr		 */
676111119Simp		cp = uma_zalloc(namei_zone, M_WAITOK);
67727446Sdfr		fromcp = cnp->cn_pnbuf;
67827446Sdfr		tocp = cp;
67927446Sdfr		if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) {
68027446Sdfr			switch ((unsigned char)*fromcp) {
68127446Sdfr			case WEBNFS_NATIVE_CHAR:
68227446Sdfr				/*
68327446Sdfr				 * 'Native' path for us is the same
68427446Sdfr				 * as a path according to the NFS spec,
68527446Sdfr				 * just skip the escape char.
68627446Sdfr				 */
68727446Sdfr				fromcp++;
68827446Sdfr				break;
68927446Sdfr			/*
69027446Sdfr			 * More may be added in the future, range 0x80-0xff
69127446Sdfr			 */
69227446Sdfr			default:
69327446Sdfr				error = EIO;
69492783Sjeff				uma_zfree(namei_zone, cp);
69527446Sdfr				goto out;
69627446Sdfr			}
69727446Sdfr		}
69827446Sdfr		/*
69927446Sdfr		 * Translate the '%' escapes, URL-style.
70027446Sdfr		 */
70127446Sdfr		while (*fromcp != '\0') {
70227446Sdfr			if (*fromcp == WEBNFS_ESC_CHAR) {
70327446Sdfr				if (fromcp[1] != '\0' && fromcp[2] != '\0') {
70427446Sdfr					fromcp++;
70527446Sdfr					*tocp++ = HEXSTRTOI(fromcp);
70627446Sdfr					fromcp += 2;
70727446Sdfr					continue;
70827446Sdfr				} else {
70927446Sdfr					error = ENOENT;
71092783Sjeff					uma_zfree(namei_zone, cp);
71127446Sdfr					goto out;
71227446Sdfr				}
71327446Sdfr			} else
71427446Sdfr				*tocp++ = *fromcp++;
71527446Sdfr		}
71627446Sdfr		*tocp = '\0';
71792783Sjeff		uma_zfree(namei_zone, cnp->cn_pnbuf);
71827446Sdfr		cnp->cn_pnbuf = cp;
71927446Sdfr	}
72027446Sdfr
72127446Sdfr	ndp->ni_pathlen = (tocp - cnp->cn_pnbuf) + 1;
72227446Sdfr	ndp->ni_segflg = UIO_SYSSPACE;
72327446Sdfr
72427446Sdfr	if (pubflag) {
72527446Sdfr		ndp->ni_rootdir = rootvnode;
72627446Sdfr		ndp->ni_loopcnt = 0;
727167665Sjeff		if (cnp->cn_pnbuf[0] == '/') {
728167665Sjeff			int tvfslocked;
729167665Sjeff
730167665Sjeff			tvfslocked = VFS_LOCK_GIANT(rootvnode->v_mount);
731167665Sjeff			VFS_UNLOCK_GIANT(vfslocked);
73227446Sdfr			dp = rootvnode;
733167665Sjeff			vfslocked = tvfslocked;
734167665Sjeff		}
73527446Sdfr	} else {
73627609Sdfr		cnp->cn_flags |= NOCROSSMOUNT;
73727446Sdfr	}
73827446Sdfr
73948125Sjulian	/*
74048125Sjulian	 * Initialize for scan, set ni_startdir and bump ref on dp again
741123608Sjhb	 * because lookup() will dereference ni_startdir.
74248125Sjulian	 */
74348125Sjulian
744183103Sattilio	cnp->cn_thread = curthread;
7459336Sdfr	VREF(dp);
74648125Sjulian	ndp->ni_startdir = dp;
74727446Sdfr
748115301Struckman	if (!lockleaf)
749115301Struckman		cnp->cn_flags |= LOCKLEAF;
75048125Sjulian	for (;;) {
75148125Sjulian		cnp->cn_nameptr = cnp->cn_pnbuf;
75248125Sjulian		/*
75348125Sjulian		 * Call lookup() to do the real work.  If an error occurs,
75448125Sjulian		 * ndp->ni_vp and ni_dvp are left uninitialized or NULL and
75548125Sjulian		 * we do not have to dereference anything before returning.
75648125Sjulian		 * In either case ni_startdir will be dereferenced and NULLed
75748125Sjulian		 * out.
75848125Sjulian		 */
759167665Sjeff		if (vfslocked)
760167665Sjeff			ndp->ni_cnd.cn_flags |= GIANTHELD;
76148125Sjulian		error = lookup(ndp);
762167665Sjeff		vfslocked = (ndp->ni_cnd.cn_flags & GIANTHELD) != 0;
763167665Sjeff		ndp->ni_cnd.cn_flags &= ~GIANTHELD;
76448125Sjulian		if (error)
76548125Sjulian			break;
76648125Sjulian
76748125Sjulian		/*
76883651Speter		 * Check for encountering a symbolic link.  Trivial
76948125Sjulian		 * termination occurs if no symlink encountered.
77048125Sjulian		 * Note: zfree is safe because error is 0, so we will
77148125Sjulian		 * not zfree it again when we break.
77248125Sjulian		 */
77348125Sjulian		if ((cnp->cn_flags & ISSYMLINK) == 0) {
77448125Sjulian			if (cnp->cn_flags & (SAVENAME | SAVESTART))
77548125Sjulian				cnp->cn_flags |= HASBUF;
77648125Sjulian			else
77792783Sjeff				uma_zfree(namei_zone, cnp->cn_pnbuf);
778115301Struckman			if (ndp->ni_vp && !lockleaf)
779175294Sattilio				VOP_UNLOCK(ndp->ni_vp, 0);
78048125Sjulian			break;
78127446Sdfr		}
78248125Sjulian
78348125Sjulian		/*
78448125Sjulian		 * Validate symlink
78548125Sjulian		 */
7861541Srgrimes		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
787175294Sattilio			VOP_UNLOCK(ndp->ni_dvp, 0);
78827446Sdfr		if (!pubflag) {
78927446Sdfr			error = EINVAL;
79048125Sjulian			goto badlink2;
79127446Sdfr		}
79227446Sdfr
79327446Sdfr		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
79427446Sdfr			error = ELOOP;
79548125Sjulian			goto badlink2;
79627446Sdfr		}
79727609Sdfr		if (ndp->ni_pathlen > 1)
798111119Simp			cp = uma_zalloc(namei_zone, M_WAITOK);
7991541Srgrimes		else
80027446Sdfr			cp = cnp->cn_pnbuf;
80127446Sdfr		aiov.iov_base = cp;
80227446Sdfr		aiov.iov_len = MAXPATHLEN;
80327446Sdfr		auio.uio_iov = &aiov;
80427446Sdfr		auio.uio_iovcnt = 1;
80527446Sdfr		auio.uio_offset = 0;
80627446Sdfr		auio.uio_rw = UIO_READ;
80727446Sdfr		auio.uio_segflg = UIO_SYSSPACE;
80899797Sdillon		auio.uio_td = NULL;
80927446Sdfr		auio.uio_resid = MAXPATHLEN;
81027446Sdfr		error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
81127446Sdfr		if (error) {
81248125Sjulian		badlink1:
81327446Sdfr			if (ndp->ni_pathlen > 1)
81492783Sjeff				uma_zfree(namei_zone, cp);
81548125Sjulian		badlink2:
816155160Sjeff			vput(ndp->ni_vp);
81748125Sjulian			vrele(ndp->ni_dvp);
81827446Sdfr			break;
81927446Sdfr		}
82027446Sdfr		linklen = MAXPATHLEN - auio.uio_resid;
82127446Sdfr		if (linklen == 0) {
82227446Sdfr			error = ENOENT;
82348125Sjulian			goto badlink1;
82427446Sdfr		}
82527446Sdfr		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
82627446Sdfr			error = ENAMETOOLONG;
82748125Sjulian			goto badlink1;
82827446Sdfr		}
82948125Sjulian
83048125Sjulian		/*
83148125Sjulian		 * Adjust or replace path
83248125Sjulian		 */
83327446Sdfr		if (ndp->ni_pathlen > 1) {
83427446Sdfr			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
83592783Sjeff			uma_zfree(namei_zone, cnp->cn_pnbuf);
83627446Sdfr			cnp->cn_pnbuf = cp;
83727446Sdfr		} else
83827446Sdfr			cnp->cn_pnbuf[linklen] = '\0';
83927446Sdfr		ndp->ni_pathlen += linklen;
84048125Sjulian
84127446Sdfr		/*
84283651Speter		 * Cleanup refs for next loop and check if root directory
84383651Speter		 * should replace current directory.  Normally ni_dvp
84448125Sjulian		 * becomes the new base directory and is cleaned up when
84548125Sjulian		 * we loop.  Explicitly null pointers after invalidation
84648125Sjulian		 * to clarify operation.
84727446Sdfr		 */
84848125Sjulian		vput(ndp->ni_vp);
84948125Sjulian		ndp->ni_vp = NULL;
85048125Sjulian
85127446Sdfr		if (cnp->cn_pnbuf[0] == '/') {
85248125Sjulian			vrele(ndp->ni_dvp);
85348125Sjulian			ndp->ni_dvp = ndp->ni_rootdir;
85448125Sjulian			VREF(ndp->ni_dvp);
85527446Sdfr		}
85648125Sjulian		ndp->ni_startdir = ndp->ni_dvp;
85748125Sjulian		ndp->ni_dvp = NULL;
8581541Srgrimes	}
859115301Struckman	if (!lockleaf)
860115301Struckman		cnp->cn_flags &= ~LOCKLEAF;
861159268Skib	if (cnp->cn_flags & GIANTHELD) {
862159268Skib		mtx_unlock(&Giant);
863159268Skib		cnp->cn_flags &= ~GIANTHELD;
864159268Skib	}
86548125Sjulian
86648125Sjulian	/*
86748125Sjulian	 * nfs_namei() guarentees that fields will not contain garbage
86848125Sjulian	 * whether an error occurs or not.  This allows the caller to track
86948125Sjulian	 * cleanup state trivially.
87048125Sjulian	 */
8711541Srgrimesout:
87248125Sjulian	if (error) {
87392783Sjeff		uma_zfree(namei_zone, cnp->cn_pnbuf);
87448125Sjulian		ndp->ni_vp = NULL;
87548125Sjulian		ndp->ni_dvp = NULL;
87648125Sjulian		ndp->ni_startdir = NULL;
87748125Sjulian		cnp->cn_flags &= ~HASBUF;
878167665Sjeff		VFS_UNLOCK_GIANT(vfslocked);
879167665Sjeff		vfslocked = 0;
88048125Sjulian	} else if ((ndp->ni_cnd.cn_flags & (WANTPARENT|LOCKPARENT)) == 0) {
88148125Sjulian		ndp->ni_dvp = NULL;
88248125Sjulian	}
883167665Sjeff	/*
884167665Sjeff	 * This differs from normal namei() in that even on failure we may
885167665Sjeff	 * return with Giant held due to the dirp return.  Make sure we only
886167665Sjeff	 * have not recursed however.  The calling code only expects to drop
887167665Sjeff	 * one acquire.
888167665Sjeff	 */
889167665Sjeff	if (vfslocked || dvfslocked)
890167665Sjeff		ndp->ni_cnd.cn_flags |= GIANTHELD;
891167665Sjeff	if (vfslocked && dvfslocked)
892167665Sjeff		VFS_UNLOCK_GIANT(vfslocked);
8931541Srgrimes	return (error);
8941541Srgrimes}
8951541Srgrimes
8961541Srgrimes/*
8971541Srgrimes * A fiddled version of m_adj() that ensures null fill to a long
8981541Srgrimes * boundary and only trims off the back end
8991541Srgrimes */
9001541Srgrimesvoid
90183651Speternfsm_adj(struct mbuf *mp, int len, int nul)
9021541Srgrimes{
90383651Speter	struct mbuf *m;
90483651Speter	int count, i;
90583651Speter	char *cp;
9061541Srgrimes
9071541Srgrimes	/*
9081541Srgrimes	 * Trim from tail.  Scan the mbuf chain,
9091541Srgrimes	 * calculating its length and finding the last mbuf.
9101541Srgrimes	 * If the adjustment only affects this mbuf, then just
9111541Srgrimes	 * adjust and return.  Otherwise, rescan and truncate
9121541Srgrimes	 * after the remaining size.
9131541Srgrimes	 */
9141541Srgrimes	count = 0;
9151541Srgrimes	m = mp;
9161541Srgrimes	for (;;) {
9171541Srgrimes		count += m->m_len;
91899797Sdillon		if (m->m_next == NULL)
9191541Srgrimes			break;
9201541Srgrimes		m = m->m_next;
9211541Srgrimes	}
9221541Srgrimes	if (m->m_len > len) {
9231541Srgrimes		m->m_len -= len;
9241541Srgrimes		if (nul > 0) {
9251541Srgrimes			cp = mtod(m, caddr_t)+m->m_len-nul;
9261541Srgrimes			for (i = 0; i < nul; i++)
9271541Srgrimes				*cp++ = '\0';
9281541Srgrimes		}
9291541Srgrimes		return;
9301541Srgrimes	}
9311541Srgrimes	count -= len;
9321541Srgrimes	if (count < 0)
9331541Srgrimes		count = 0;
9341541Srgrimes	/*
9351541Srgrimes	 * Correct length for chain is "count".
9361541Srgrimes	 * Find the mbuf with last data, adjust its length,
9371541Srgrimes	 * and toss data from remaining mbufs on chain.
9381541Srgrimes	 */
9391541Srgrimes	for (m = mp; m; m = m->m_next) {
9401541Srgrimes		if (m->m_len >= count) {
9411541Srgrimes			m->m_len = count;
9421541Srgrimes			if (nul > 0) {
9431541Srgrimes				cp = mtod(m, caddr_t)+m->m_len-nul;
9441541Srgrimes				for (i = 0; i < nul; i++)
9451541Srgrimes					*cp++ = '\0';
9461541Srgrimes			}
947144246Ssam			if (m->m_next != NULL) {
948144246Ssam				m_freem(m->m_next);
949144246Ssam				m->m_next = NULL;
950144246Ssam			}
9511541Srgrimes			break;
9521541Srgrimes		}
9531541Srgrimes		count -= m->m_len;
9541541Srgrimes	}
9551541Srgrimes}
9561541Srgrimes
9571541Srgrimes/*
9589336Sdfr * Make these functions instead of macros, so that the kernel text size
9599336Sdfr * doesn't get too big...
9609336Sdfr */
9619336Sdfrvoid
96283651Speternfsm_srvwcc(struct nfsrv_descript *nfsd, int before_ret,
96383651Speter    struct vattr *before_vap, int after_ret, struct vattr *after_vap,
96483651Speter    struct mbuf **mbp, char **bposp)
9659336Sdfr{
96683651Speter	struct mbuf *mb = *mbp;
96783651Speter	char *bpos = *bposp;
96883651Speter	u_int32_t *tl;
9699336Sdfr
9709336Sdfr	if (before_ret) {
97184002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
97289094Smsmith		*tl = nfsrv_nfs_false;
9739336Sdfr	} else {
97484002Speter		tl = nfsm_build(u_int32_t *, 7 * NFSX_UNSIGNED);
97589094Smsmith		*tl++ = nfsrv_nfs_true;
97647751Speter		txdr_hyper(before_vap->va_size, tl);
9779336Sdfr		tl += 2;
9789336Sdfr		txdr_nfsv3time(&(before_vap->va_mtime), tl);
9799336Sdfr		tl += 2;
9809336Sdfr		txdr_nfsv3time(&(before_vap->va_ctime), tl);
9819336Sdfr	}
9829336Sdfr	*bposp = bpos;
9839336Sdfr	*mbp = mb;
9849336Sdfr	nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp);
9859336Sdfr}
9869336Sdfr
9879336Sdfrvoid
98883651Speternfsm_srvpostopattr(struct nfsrv_descript *nfsd, int after_ret,
98983651Speter    struct vattr *after_vap, struct mbuf **mbp, char **bposp)
9909336Sdfr{
99183651Speter	struct mbuf *mb = *mbp;
99283651Speter	char *bpos = *bposp;
99383651Speter	u_int32_t *tl;
99483651Speter	struct nfs_fattr *fp;
9959336Sdfr
9969336Sdfr	if (after_ret) {
99784002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
99889094Smsmith		*tl = nfsrv_nfs_false;
9999336Sdfr	} else {
100084002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED + NFSX_V3FATTR);
100189094Smsmith		*tl++ = nfsrv_nfs_true;
10029336Sdfr		fp = (struct nfs_fattr *)tl;
10039336Sdfr		nfsm_srvfattr(nfsd, after_vap, fp);
10049336Sdfr	}
10059336Sdfr	*mbp = mb;
10069336Sdfr	*bposp = bpos;
10079336Sdfr}
10089336Sdfr
10099336Sdfrvoid
101083651Speternfsm_srvfattr(struct nfsrv_descript *nfsd, struct vattr *vap,
101183651Speter    struct nfs_fattr *fp)
10129336Sdfr{
10139336Sdfr
10149336Sdfr	fp->fa_nlink = txdr_unsigned(vap->va_nlink);
10159336Sdfr	fp->fa_uid = txdr_unsigned(vap->va_uid);
10169336Sdfr	fp->fa_gid = txdr_unsigned(vap->va_gid);
10179336Sdfr	if (nfsd->nd_flag & ND_NFSV3) {
10189336Sdfr		fp->fa_type = vtonfsv3_type(vap->va_type);
10199336Sdfr		fp->fa_mode = vtonfsv3_mode(vap->va_mode);
102047751Speter		txdr_hyper(vap->va_size, &fp->fa3_size);
102147751Speter		txdr_hyper(vap->va_bytes, &fp->fa3_used);
1022187830Sed		fp->fa3_rdev.specdata1 = txdr_unsigned(major(vap->va_rdev));
1023187830Sed		fp->fa3_rdev.specdata2 = txdr_unsigned(minor(vap->va_rdev));
10249336Sdfr		fp->fa3_fsid.nfsuquad[0] = 0;
10259336Sdfr		fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
10269336Sdfr		fp->fa3_fileid.nfsuquad[0] = 0;
10279336Sdfr		fp->fa3_fileid.nfsuquad[1] = txdr_unsigned(vap->va_fileid);
10289336Sdfr		txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
10299336Sdfr		txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
10309336Sdfr		txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
10319336Sdfr	} else {
10329336Sdfr		fp->fa_type = vtonfsv2_type(vap->va_type);
10339336Sdfr		fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
10349336Sdfr		fp->fa2_size = txdr_unsigned(vap->va_size);
10359336Sdfr		fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
10369336Sdfr		if (vap->va_type == VFIFO)
10379336Sdfr			fp->fa2_rdev = 0xffffffff;
10389336Sdfr		else
10399336Sdfr			fp->fa2_rdev = txdr_unsigned(vap->va_rdev);
10409336Sdfr		fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
10419336Sdfr		fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
10429336Sdfr		fp->fa2_fileid = txdr_unsigned(vap->va_fileid);
10439336Sdfr		txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
10449336Sdfr		txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
10459336Sdfr		txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
10469336Sdfr	}
10479336Sdfr}
10489336Sdfr
10499336Sdfr/*
10501541Srgrimes * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
10511541Srgrimes * 	- look up fsid in mount list (if not found ret error)
10521541Srgrimes *	- get vp and export rights by calling VFS_FHTOVP()
10531541Srgrimes *	- if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
10541541Srgrimes *	- if not lockflag unlock it with VOP_UNLOCK()
10551541Srgrimes */
10561549Srgrimesint
1057167665Sjeffnfsrv_fhtovp(fhandle_t *fhp, int lockflag, struct vnode **vpp, int *vfslockedp,
1058184588Sdfr    struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
1059184588Sdfr    struct sockaddr *nam, int *rdonlyp, int pubflag)
10601541Srgrimes{
106183651Speter	struct mount *mp;
106283651Speter	int i;
1063184588Sdfr	struct ucred *cred, *credanon;
10641541Srgrimes	int error, exflags;
106536534Speter#ifdef MNT_EXNORESPORT		/* XXX needs mountd and /etc/exports help yet */
106636534Speter	struct sockaddr_int *saddr;
106736534Speter#endif
1068184588Sdfr	int credflavor;
1069167665Sjeff	int vfslocked;
1070184588Sdfr	int numsecflavors, *secflavors;
1071184693Sdfr	int authsys;
1072184588Sdfr	int v3 = nfsd->nd_flag & ND_NFSV3;
1073184588Sdfr	int mountreq;
10741541Srgrimes
1075167665Sjeff	*vfslockedp = 0;
107699797Sdillon	*vpp = NULL;
107727446Sdfr
107827446Sdfr	if (nfs_ispublicfh(fhp)) {
107927446Sdfr		if (!pubflag || !nfs_pub.np_valid)
108027446Sdfr			return (ESTALE);
108127446Sdfr		fhp = &nfs_pub.np_handle;
108227446Sdfr	}
108327446Sdfr
1084185432Skib	mp = vfs_busyfs(&fhp->fh_fsid);
10853305Sphk	if (!mp)
10861541Srgrimes		return (ESTALE);
1087157325Sjeff	vfslocked = VFS_LOCK_GIANT(mp);
1088184588Sdfr	error = VFS_CHECKEXP(mp, nam, &exflags, &credanon,
1089184588Sdfr	    &numsecflavors, &secflavors);
1090185432Skib	if (error) {
1091185432Skib		vfs_unbusy(mp);
1092129639Srwatson		goto out;
1093185432Skib	}
1094184693Sdfr	if (numsecflavors == 0) {
1095184693Sdfr		/*
1096184693Sdfr		 * This can happen if the system is running with an
1097184693Sdfr		 * old mountd that doesn't pass in a secflavor list.
1098184693Sdfr		 */
1099184693Sdfr		numsecflavors = 1;
1100195202Sdfr		authsys = AUTH_SYS;
1101184693Sdfr		secflavors = &authsys;
1102184693Sdfr	}
1103184588Sdfr	credflavor = nfsd->nd_credflavor;
1104184588Sdfr	for (i = 0; i < numsecflavors; i++) {
1105184588Sdfr		if (secflavors[i] == credflavor)
1106184588Sdfr			break;
1107184588Sdfr	}
1108184588Sdfr	if (i == numsecflavors) {
1109184588Sdfr		/*
1110184588Sdfr		 * RFC 2623 section 2.3.2 - allow certain procedures
1111184588Sdfr		 * used at NFS client mount time even if they have
1112184588Sdfr		 * weak authentication.
1113184588Sdfr		 */
1114184588Sdfr		mountreq = FALSE;
1115184588Sdfr		if (v3) {
1116184868Sdfr			if (nfsd->nd_procnum == NFSPROC_FSINFO
1117184868Sdfr			    || nfsd->nd_procnum == NFSPROC_GETATTR)
1118184588Sdfr				mountreq = TRUE;
1119184588Sdfr		} else {
1120184588Sdfr			if (nfsd->nd_procnum == NFSPROC_FSSTAT
1121184588Sdfr			    || nfsd->nd_procnum == NFSPROC_GETATTR)
1122184588Sdfr				mountreq = TRUE;
1123184588Sdfr		}
1124184588Sdfr		if (!mountreq) {
1125184868Sdfr			error = NFSERR_AUTHERR | AUTH_TOOWEAK;
1126185432Skib			vfs_unbusy(mp);
1127184588Sdfr			goto out;
1128184588Sdfr		}
1129184588Sdfr	}
113051138Salfred	error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp);
1131205661Srmacklem	if (error != 0)
1132205661Srmacklem		/* Make sure the server replies ESTALE to the client. */
1133205661Srmacklem		error = ESTALE;
1134185432Skib	vfs_unbusy(mp);
113551138Salfred	if (error)
1136129639Srwatson		goto out;
113736534Speter#ifdef MNT_EXNORESPORT
113836534Speter	if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) {
113936534Speter		saddr = (struct sockaddr_in *)nam;
1140100134Salfred		if ((saddr->sin_family == AF_INET ||
1141100134Salfred		     saddr->sin_family == AF_INET6) &&
1142100134Salfred	/* same code for INET and INET6: sin*_port at same offet */
114336534Speter		    ntohs(saddr->sin_port) >= IPPORT_RESERVED) {
114436534Speter			vput(*vpp);
114554485Sdillon			*vpp = NULL;
1146129639Srwatson			error = NFSERR_AUTHERR | AUTH_TOOWEAK;
114736534Speter		}
114836534Speter	}
114936534Speter#endif
11501541Srgrimes	/*
11511541Srgrimes	 * Check/setup credentials.
11521541Srgrimes	 */
1153184588Sdfr	cred = nfsd->nd_cr;
115483651Speter	if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
11551541Srgrimes		cred->cr_uid = credanon->cr_uid;
1156194498Sbrooks		crsetgroups(cred, credanon->cr_ngroups, credanon->cr_groups);
11571541Srgrimes	}
11581541Srgrimes	if (exflags & MNT_EXRDONLY)
11591541Srgrimes		*rdonlyp = 1;
11601541Srgrimes	else
11611541Srgrimes		*rdonlyp = 0;
11627969Sdyson
11631541Srgrimes	if (!lockflag)
1164175294Sattilio		VOP_UNLOCK(*vpp, 0);
1165129639Srwatsonout:
1166191940Skan	if (credanon != NULL)
1167191940Skan		crfree(credanon);
1168191940Skan
1169167665Sjeff	if (error) {
1170167665Sjeff		VFS_UNLOCK_GIANT(vfslocked);
1171167665Sjeff	} else
1172167665Sjeff		*vfslockedp = vfslocked;
1173164585Srwatson	return (error);
1174164585Srwatson}
1175164585Srwatson
1176164585Srwatson
117727446Sdfr/*
117827446Sdfr * WebNFS: check if a filehandle is a public filehandle. For v3, this
117927446Sdfr * means a length of 0, for v2 it means all zeroes. nfsm_srvmtofh has
118027446Sdfr * transformed this to all zeroes in both cases, so check for it.
118127446Sdfr */
118227446Sdfrint
118383651Speternfs_ispublicfh(fhandle_t *fhp)
118427446Sdfr{
118527446Sdfr	char *cp = (char *)fhp;
118627446Sdfr	int i;
118727446Sdfr
1188129639Srwatson	NFSD_LOCK_DONTCARE();
1189129639Srwatson
119027446Sdfr	for (i = 0; i < NFSX_V3FH; i++)
119127446Sdfr		if (*cp++ != 0)
119227446Sdfr			return (FALSE);
119327446Sdfr	return (TRUE);
119427446Sdfr}
119583651Speter
11961541Srgrimes/*
11979336Sdfr * Map errnos to NFS error numbers. For Version 3 also filter out error
11989336Sdfr * numbers not specified for the associated procedure.
11999336Sdfr */
12005455Sdgint
120183651Speternfsrv_errmap(struct nfsrv_descript *nd, int err)
12029336Sdfr{
1203129639Srwatson	const short *defaulterrp, *errp;
1204102236Sphk	int e;
12059336Sdfr
1206129639Srwatson
12079336Sdfr	if (nd->nd_flag & ND_NFSV3) {
12089336Sdfr	    if (nd->nd_procnum <= NFSPROC_COMMIT) {
12099336Sdfr		errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
12109336Sdfr		while (*++errp) {
12119336Sdfr			if (*errp == err)
12129336Sdfr				return (err);
12139336Sdfr			else if (*errp > err)
12149336Sdfr				break;
12159336Sdfr		}
12169336Sdfr		return ((int)*defaulterrp);
12179336Sdfr	    } else
12189336Sdfr		return (err & 0xffff);
12199336Sdfr	}
1220102236Sphk	e = 0;
12219336Sdfr	if (err <= ELAST)
1222102236Sphk		e = nfsrv_v2errmap[err - 1];
1223102236Sphk	if (e != 0)
1224102236Sphk		return (e);
12259336Sdfr	return (NFSERR_IO);
12269336Sdfr}
12279336Sdfr
122836503Speter/*
122936503Speter * Sort the group list in increasing numerical order.
123036503Speter * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
123136503Speter *  that used to be here.)
123236503Speter */
123336503Spetervoid
123483651Speternfsrvw_sort(gid_t *list, int num)
123536503Speter{
123683651Speter	int i, j;
123736503Speter	gid_t v;
123836503Speter
123936503Speter	/* Insertion sort. */
124036503Speter	for (i = 1; i < num; i++) {
124136503Speter		v = list[i];
124236503Speter		/* find correct slot for value v, moving others up */
124336503Speter		for (j = i; --j >= 0 && v < list[j];)
124436503Speter			list[j + 1] = list[j];
124536503Speter		list[j + 1] = v;
124636503Speter	}
124736503Speter}
124836503Speter
124936503Speter/*
125083651Speter * Helper functions for macros.
125183651Speter */
125283651Speter
125383651Spetervoid
125488091Siedowsenfsm_srvfhtom_xx(fhandle_t *f, int v3, struct mbuf **mb, caddr_t *bpos)
125583651Speter{
125688091Siedowse	u_int32_t *tl;
125783651Speter
125883651Speter	if (v3) {
125988091Siedowse		tl = nfsm_build_xx(NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
126088091Siedowse		*tl++ = txdr_unsigned(NFSX_V3FH);
126188091Siedowse		bcopy(f, tl, NFSX_V3FH);
126283651Speter	} else {
126388091Siedowse		tl = nfsm_build_xx(NFSX_V2FH, mb, bpos);
126488091Siedowse		bcopy(f, tl, NFSX_V2FH);
126583651Speter	}
126683651Speter}
126783651Speter
126883651Spetervoid
126988091Siedowsenfsm_srvpostop_fh_xx(fhandle_t *f, struct mbuf **mb, caddr_t *bpos)
127083651Speter{
127188091Siedowse	u_int32_t *tl;
127284002Speter
127388091Siedowse	tl = nfsm_build_xx(2 * NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
127489094Smsmith	*tl++ = nfsrv_nfs_true;
127588091Siedowse	*tl++ = txdr_unsigned(NFSX_V3FH);
127688091Siedowse	bcopy(f, tl, NFSX_V3FH);
127783651Speter}
127883651Speter
127983651Speterint
128088091Siedowsenfsm_srvstrsiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
128183651Speter{
128288091Siedowse	u_int32_t *tl;
128383651Speter
1284140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
128588091Siedowse	if (tl == NULL)
128684057Speter		return EBADRPC;
128788091Siedowse	*s = fxdr_unsigned(int32_t, *tl);
128883651Speter	if (*s > m || *s <= 0)
128983651Speter		return EBADRPC;
129083651Speter	return 0;
129183651Speter}
129283651Speter
129383651Speterint
1294106264Sjeffnfsm_srvnamesiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
129583651Speter{
129688091Siedowse	u_int32_t *tl;
129783651Speter
1298129639Srwatson	NFSD_LOCK_DONTCARE();
1299129639Srwatson
1300140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
130188091Siedowse	if (tl == NULL)
130284057Speter		return EBADRPC;
130388091Siedowse	*s = fxdr_unsigned(int32_t, *tl);
1304106264Sjeff	if (*s > m)
130583651Speter		return NFSERR_NAMETOL;
130683651Speter	if (*s <= 0)
130783651Speter		return EBADRPC;
130883651Speter	return 0;
130983651Speter}
131083651Speter
1311165739Shrsint
1312165739Shrsnfsm_srvnamesiz0_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
1313165739Shrs{
1314165739Shrs	u_int32_t *tl;
1315165739Shrs
1316165739Shrs	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1317165739Shrs	if (tl == NULL)
1318165739Shrs		return EBADRPC;
1319165739Shrs	*s = fxdr_unsigned(int32_t, *tl);
1320165739Shrs	if (*s > m)
1321165739Shrs		return NFSERR_NAMETOL;
1322165739Shrs	if (*s < 0)
1323165739Shrs		return EBADRPC;
1324165739Shrs	return 0;
1325165739Shrs}
1326165739Shrs
132783651Spetervoid
132883651Speternfsm_clget_xx(u_int32_t **tl, struct mbuf *mb, struct mbuf **mp,
1329167665Sjeff    char **bp, char **be, caddr_t bpos)
133083651Speter{
133183651Speter	struct mbuf *nmp;
133283651Speter
1333167665Sjeff	NFSD_UNLOCK_ASSERT();
1334129639Srwatson
133583651Speter	if (*bp >= *be) {
133683651Speter		if (*mp == mb)
133783651Speter			(*mp)->m_len += *bp - bpos;
1338177599Sru		MGET(nmp, M_WAIT, MT_DATA);
1339177599Sru		MCLGET(nmp, M_WAIT);
134083651Speter		nmp->m_len = NFSMSIZ(nmp);
134183651Speter		(*mp)->m_next = nmp;
134283651Speter		*mp = nmp;
134383651Speter		*bp = mtod(*mp, caddr_t);
134483651Speter		*be = *bp + (*mp)->m_len;
134583651Speter	}
134683651Speter	*tl = (u_int32_t *)*bp;
134783651Speter}
134883651Speter
134983651Speterint
1350184588Sdfrnfsm_srvmtofh_xx(fhandle_t *f, int v3, struct mbuf **md, caddr_t *dpos)
135183651Speter{
135288091Siedowse	u_int32_t *tl;
135383651Speter	int fhlen;
135483651Speter
1355184588Sdfr	if (v3) {
1356140495Sps		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
135788091Siedowse		if (tl == NULL)
135884057Speter			return EBADRPC;
135988091Siedowse		fhlen = fxdr_unsigned(int, *tl);
136083651Speter		if (fhlen != 0 && fhlen != NFSX_V3FH)
136183651Speter			return EBADRPC;
136283651Speter	} else {
136383651Speter		fhlen = NFSX_V2FH;
136483651Speter	}
136583651Speter	if (fhlen != 0) {
1366140495Sps		tl = nfsm_dissect_xx_nonblock(fhlen, md, dpos);
136788091Siedowse		if (tl == NULL)
136884057Speter			return EBADRPC;
136988091Siedowse		bcopy((caddr_t)tl, (caddr_t)(f), fhlen);
137083651Speter	} else {
137183651Speter		bzero((caddr_t)(f), NFSX_V3FH);
137283651Speter	}
137383651Speter	return 0;
137483651Speter}
137583651Speter
137683651Speterint
137788091Siedowsenfsm_srvsattr_xx(struct vattr *a, struct mbuf **md, caddr_t *dpos)
137883651Speter{
137988091Siedowse	u_int32_t *tl;
1380157391Scel	int toclient = 0;
138183651Speter
1382140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
138388091Siedowse	if (tl == NULL)
138484057Speter		return EBADRPC;
138589094Smsmith	if (*tl == nfsrv_nfs_true) {
1386140495Sps		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
138788091Siedowse		if (tl == NULL)
138884057Speter			return EBADRPC;
138988091Siedowse		(a)->va_mode = nfstov_mode(*tl);
139083651Speter	}
1391140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
139288091Siedowse	if (tl == NULL)
139384057Speter		return EBADRPC;
139489094Smsmith	if (*tl == nfsrv_nfs_true) {
1395140495Sps		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
139688091Siedowse		if (tl == NULL)
139784057Speter			return EBADRPC;
139888091Siedowse		(a)->va_uid = fxdr_unsigned(uid_t, *tl);
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_gid = fxdr_unsigned(gid_t, *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(2 * NFSX_UNSIGNED, md, dpos);
141488091Siedowse		if (tl == NULL)
141584057Speter			return EBADRPC;
141688091Siedowse		(a)->va_size = fxdr_hyper(tl);
141783651Speter	}
1418140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
141988091Siedowse	if (tl == NULL)
142084057Speter		return EBADRPC;
142188091Siedowse	switch (fxdr_unsigned(int, *tl)) {
142283651Speter	case NFSV3SATTRTIME_TOCLIENT:
1423140495Sps		tl = nfsm_dissect_xx_nonblock(2 * NFSX_UNSIGNED, md, dpos);
142488091Siedowse		if (tl == NULL)
142584057Speter			return EBADRPC;
142688091Siedowse		fxdr_nfsv3time(tl, &(a)->va_atime);
1427157391Scel		toclient = 1;
142883651Speter		break;
142983651Speter	case NFSV3SATTRTIME_TOSERVER:
143083651Speter		getnanotime(&(a)->va_atime);
1431157391Scel		a->va_vaflags |= VA_UTIMES_NULL;
143283651Speter		break;
143383651Speter	}
1434140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
143588091Siedowse	if (tl == NULL)
143684057Speter		return EBADRPC;
143788091Siedowse	switch (fxdr_unsigned(int, *tl)) {
143883651Speter	case NFSV3SATTRTIME_TOCLIENT:
1439140495Sps		tl = nfsm_dissect_xx_nonblock(2 * NFSX_UNSIGNED, md, dpos);
144088091Siedowse		if (tl == NULL)
144184057Speter			return EBADRPC;
144288091Siedowse		fxdr_nfsv3time(tl, &(a)->va_mtime);
1443157391Scel		a->va_vaflags &= ~VA_UTIMES_NULL;
144483651Speter		break;
144583651Speter	case NFSV3SATTRTIME_TOSERVER:
144683651Speter		getnanotime(&(a)->va_mtime);
1447157391Scel		if (toclient == 0)
1448157391Scel			a->va_vaflags |= VA_UTIMES_NULL;
144983651Speter		break;
145083651Speter	}
145183651Speter	return 0;
145283651Speter}
1453