nfs_srvsubs.c revision 159268
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 159268 2006-06-05 14:48:02Z kib $");
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
10138894Sbdestatic int nfs_prev_nfssvc_sy_narg;
10238894Sbdestatic sy_call_t *nfs_prev_nfssvc_sy_call;
10338894Sbde
104129639Srwatsonstruct mtx nfsd_mtx;
105129639Srwatson
1069336Sdfr/*
1079336Sdfr * Mapping of old NFS Version 2 RPC numbers to generic numbers.
1089336Sdfr */
109129639Srwatsonconst int nfsrv_nfsv3_procid[NFS_NPROCS] = {
1109336Sdfr	NFSPROC_NULL,
1119336Sdfr	NFSPROC_GETATTR,
1129336Sdfr	NFSPROC_SETATTR,
1139336Sdfr	NFSPROC_NOOP,
1149336Sdfr	NFSPROC_LOOKUP,
1159336Sdfr	NFSPROC_READLINK,
1169336Sdfr	NFSPROC_READ,
1179336Sdfr	NFSPROC_NOOP,
1189336Sdfr	NFSPROC_WRITE,
1199336Sdfr	NFSPROC_CREATE,
1209336Sdfr	NFSPROC_REMOVE,
1219336Sdfr	NFSPROC_RENAME,
1229336Sdfr	NFSPROC_LINK,
1239336Sdfr	NFSPROC_SYMLINK,
1249336Sdfr	NFSPROC_MKDIR,
1259336Sdfr	NFSPROC_RMDIR,
1269336Sdfr	NFSPROC_READDIR,
1279336Sdfr	NFSPROC_FSSTAT,
1289336Sdfr	NFSPROC_NOOP,
1299336Sdfr	NFSPROC_NOOP,
1309336Sdfr	NFSPROC_NOOP,
1319336Sdfr	NFSPROC_NOOP,
1329336Sdfr	NFSPROC_NOOP,
1339336Sdfr};
1349336Sdfr
1359336Sdfr/*
1369336Sdfr * and the reverse mapping from generic to Version 2 procedure numbers
1379336Sdfr */
138129639Srwatsonconst int nfsrvv2_procid[NFS_NPROCS] = {
1399336Sdfr	NFSV2PROC_NULL,
1409336Sdfr	NFSV2PROC_GETATTR,
1419336Sdfr	NFSV2PROC_SETATTR,
1429336Sdfr	NFSV2PROC_LOOKUP,
1439336Sdfr	NFSV2PROC_NOOP,
1449336Sdfr	NFSV2PROC_READLINK,
1459336Sdfr	NFSV2PROC_READ,
1469336Sdfr	NFSV2PROC_WRITE,
1479336Sdfr	NFSV2PROC_CREATE,
1489336Sdfr	NFSV2PROC_MKDIR,
1499336Sdfr	NFSV2PROC_SYMLINK,
1509336Sdfr	NFSV2PROC_CREATE,
1519336Sdfr	NFSV2PROC_REMOVE,
1529336Sdfr	NFSV2PROC_RMDIR,
1539336Sdfr	NFSV2PROC_RENAME,
1549336Sdfr	NFSV2PROC_LINK,
1559336Sdfr	NFSV2PROC_READDIR,
1569336Sdfr	NFSV2PROC_NOOP,
1579336Sdfr	NFSV2PROC_STATFS,
1589336Sdfr	NFSV2PROC_NOOP,
1599336Sdfr	NFSV2PROC_NOOP,
1609336Sdfr	NFSV2PROC_NOOP,
1619336Sdfr	NFSV2PROC_NOOP,
1629336Sdfr};
1639336Sdfr
1649336Sdfr/*
1659336Sdfr * Maps errno values to nfs error numbers.
166102236Sphk * Use 0 (which gets converted to NFSERR_IO) as the catch all for ones not
167102236Sphk * specifically defined in RFC 1094.
1689336Sdfr */
169129639Srwatsonstatic const u_char nfsrv_v2errmap[ELAST] = {
170102236Sphk  NFSERR_PERM,	NFSERR_NOENT,	0,		0,		0,
171102236Sphk  NFSERR_NXIO,	0,		0,		0,		0,
172102236Sphk  0,		0,		NFSERR_ACCES,	0,		0,
173102236Sphk  0,		NFSERR_EXIST,	0,		NFSERR_NODEV,	NFSERR_NOTDIR,
174102236Sphk  NFSERR_ISDIR,	0,		0,		0,		0,
175102236Sphk  0,		NFSERR_FBIG,	NFSERR_NOSPC,	0,		NFSERR_ROFS,
176102236Sphk  0,		0,		0,		0,		0,
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,		NFSERR_NAMETOL,	0,		0,
183102236Sphk  NFSERR_NOTEMPTY, 0,		0,		NFSERR_DQUOT,	NFSERR_STALE,
184102236Sphk  0
1859336Sdfr};
1869336Sdfr
1879336Sdfr/*
1889336Sdfr * Maps errno values to nfs error numbers.
1899336Sdfr * Although it is not obvious whether or not NFS clients really care if
1909336Sdfr * a returned error value is in the specified list for the procedure, the
1919336Sdfr * safest thing to do is filter them appropriately. For Version 2, the
1929336Sdfr * X/Open XNFS document is the only specification that defines error values
1939336Sdfr * for each RPC (The RFC simply lists all possible error values for all RPCs),
1949336Sdfr * so I have decided to not do this for Version 2.
1959336Sdfr * The first entry is the default error return and the rest are the valid
1969336Sdfr * errors for that RPC in increasing numeric order.
1979336Sdfr */
198129639Srwatsonstatic const short nfsv3err_null[] = {
1999336Sdfr	0,
2009336Sdfr	0,
2019336Sdfr};
2029336Sdfr
203129639Srwatsonstatic const short nfsv3err_getattr[] = {
2049336Sdfr	NFSERR_IO,
2059336Sdfr	NFSERR_IO,
2069336Sdfr	NFSERR_STALE,
2079336Sdfr	NFSERR_BADHANDLE,
2089336Sdfr	NFSERR_SERVERFAULT,
2099336Sdfr	0,
2109336Sdfr};
2119336Sdfr
212129639Srwatsonstatic const short nfsv3err_setattr[] = {
2139336Sdfr	NFSERR_IO,
2149336Sdfr	NFSERR_PERM,
2159336Sdfr	NFSERR_IO,
2169336Sdfr	NFSERR_ACCES,
2179336Sdfr	NFSERR_INVAL,
2189336Sdfr	NFSERR_NOSPC,
2199336Sdfr	NFSERR_ROFS,
2209336Sdfr	NFSERR_DQUOT,
2219336Sdfr	NFSERR_STALE,
2229336Sdfr	NFSERR_BADHANDLE,
2239336Sdfr	NFSERR_NOT_SYNC,
2249336Sdfr	NFSERR_SERVERFAULT,
2259336Sdfr	0,
2269336Sdfr};
2279336Sdfr
228129639Srwatsonstatic const short nfsv3err_lookup[] = {
2299336Sdfr	NFSERR_IO,
2309336Sdfr	NFSERR_NOENT,
2319336Sdfr	NFSERR_IO,
2329336Sdfr	NFSERR_ACCES,
2339336Sdfr	NFSERR_NOTDIR,
2349336Sdfr	NFSERR_NAMETOL,
2359336Sdfr	NFSERR_STALE,
2369336Sdfr	NFSERR_BADHANDLE,
2379336Sdfr	NFSERR_SERVERFAULT,
2389336Sdfr	0,
2399336Sdfr};
2409336Sdfr
241129639Srwatsonstatic const short nfsv3err_access[] = {
2429336Sdfr	NFSERR_IO,
2439336Sdfr	NFSERR_IO,
2449336Sdfr	NFSERR_STALE,
2459336Sdfr	NFSERR_BADHANDLE,
2469336Sdfr	NFSERR_SERVERFAULT,
2479336Sdfr	0,
2489336Sdfr};
2499336Sdfr
250129639Srwatsonstatic const short nfsv3err_readlink[] = {
2519336Sdfr	NFSERR_IO,
2529336Sdfr	NFSERR_IO,
2539336Sdfr	NFSERR_ACCES,
2549336Sdfr	NFSERR_INVAL,
2559336Sdfr	NFSERR_STALE,
2569336Sdfr	NFSERR_BADHANDLE,
2579336Sdfr	NFSERR_NOTSUPP,
2589336Sdfr	NFSERR_SERVERFAULT,
2599336Sdfr	0,
2609336Sdfr};
2619336Sdfr
262129639Srwatsonstatic const short nfsv3err_read[] = {
2639336Sdfr	NFSERR_IO,
2649336Sdfr	NFSERR_IO,
2659336Sdfr	NFSERR_NXIO,
2669336Sdfr	NFSERR_ACCES,
2679336Sdfr	NFSERR_INVAL,
2689336Sdfr	NFSERR_STALE,
2699336Sdfr	NFSERR_BADHANDLE,
2709336Sdfr	NFSERR_SERVERFAULT,
2719336Sdfr	0,
2729336Sdfr};
2739336Sdfr
274129639Srwatsonstatic const short nfsv3err_write[] = {
2759336Sdfr	NFSERR_IO,
2769336Sdfr	NFSERR_IO,
2779336Sdfr	NFSERR_ACCES,
2789336Sdfr	NFSERR_INVAL,
2799336Sdfr	NFSERR_FBIG,
2809336Sdfr	NFSERR_NOSPC,
2819336Sdfr	NFSERR_ROFS,
2829336Sdfr	NFSERR_DQUOT,
2839336Sdfr	NFSERR_STALE,
2849336Sdfr	NFSERR_BADHANDLE,
2859336Sdfr	NFSERR_SERVERFAULT,
2869336Sdfr	0,
2879336Sdfr};
2889336Sdfr
289129639Srwatsonstatic const short nfsv3err_create[] = {
2909336Sdfr	NFSERR_IO,
2919336Sdfr	NFSERR_IO,
2929336Sdfr	NFSERR_ACCES,
2939336Sdfr	NFSERR_EXIST,
2949336Sdfr	NFSERR_NOTDIR,
2959336Sdfr	NFSERR_NOSPC,
2969336Sdfr	NFSERR_ROFS,
2979336Sdfr	NFSERR_NAMETOL,
2989336Sdfr	NFSERR_DQUOT,
2999336Sdfr	NFSERR_STALE,
3009336Sdfr	NFSERR_BADHANDLE,
3019336Sdfr	NFSERR_NOTSUPP,
3029336Sdfr	NFSERR_SERVERFAULT,
3039336Sdfr	0,
3049336Sdfr};
3059336Sdfr
306129639Srwatsonstatic const short nfsv3err_mkdir[] = {
3079336Sdfr	NFSERR_IO,
3089336Sdfr	NFSERR_IO,
3099336Sdfr	NFSERR_ACCES,
3109336Sdfr	NFSERR_EXIST,
3119336Sdfr	NFSERR_NOTDIR,
3129336Sdfr	NFSERR_NOSPC,
3139336Sdfr	NFSERR_ROFS,
3149336Sdfr	NFSERR_NAMETOL,
3159336Sdfr	NFSERR_DQUOT,
3169336Sdfr	NFSERR_STALE,
3179336Sdfr	NFSERR_BADHANDLE,
3189336Sdfr	NFSERR_NOTSUPP,
3199336Sdfr	NFSERR_SERVERFAULT,
3209336Sdfr	0,
3219336Sdfr};
3229336Sdfr
323129639Srwatsonstatic const short nfsv3err_symlink[] = {
3249336Sdfr	NFSERR_IO,
3259336Sdfr	NFSERR_IO,
3269336Sdfr	NFSERR_ACCES,
3279336Sdfr	NFSERR_EXIST,
3289336Sdfr	NFSERR_NOTDIR,
3299336Sdfr	NFSERR_NOSPC,
3309336Sdfr	NFSERR_ROFS,
3319336Sdfr	NFSERR_NAMETOL,
3329336Sdfr	NFSERR_DQUOT,
3339336Sdfr	NFSERR_STALE,
3349336Sdfr	NFSERR_BADHANDLE,
3359336Sdfr	NFSERR_NOTSUPP,
3369336Sdfr	NFSERR_SERVERFAULT,
3379336Sdfr	0,
3389336Sdfr};
3399336Sdfr
340129639Srwatsonstatic const short nfsv3err_mknod[] = {
3419336Sdfr	NFSERR_IO,
3429336Sdfr	NFSERR_IO,
3439336Sdfr	NFSERR_ACCES,
3449336Sdfr	NFSERR_EXIST,
3459336Sdfr	NFSERR_NOTDIR,
3469336Sdfr	NFSERR_NOSPC,
3479336Sdfr	NFSERR_ROFS,
3489336Sdfr	NFSERR_NAMETOL,
3499336Sdfr	NFSERR_DQUOT,
3509336Sdfr	NFSERR_STALE,
3519336Sdfr	NFSERR_BADHANDLE,
3529336Sdfr	NFSERR_NOTSUPP,
3539336Sdfr	NFSERR_SERVERFAULT,
3549336Sdfr	NFSERR_BADTYPE,
3559336Sdfr	0,
3569336Sdfr};
3579336Sdfr
358129639Srwatsonstatic const short nfsv3err_remove[] = {
3599336Sdfr	NFSERR_IO,
3609336Sdfr	NFSERR_NOENT,
3619336Sdfr	NFSERR_IO,
3629336Sdfr	NFSERR_ACCES,
3639336Sdfr	NFSERR_NOTDIR,
3649336Sdfr	NFSERR_ROFS,
3659336Sdfr	NFSERR_NAMETOL,
3669336Sdfr	NFSERR_STALE,
3679336Sdfr	NFSERR_BADHANDLE,
3689336Sdfr	NFSERR_SERVERFAULT,
3699336Sdfr	0,
3709336Sdfr};
3719336Sdfr
372129639Srwatsonstatic const short nfsv3err_rmdir[] = {
3739336Sdfr	NFSERR_IO,
3749336Sdfr	NFSERR_NOENT,
3759336Sdfr	NFSERR_IO,
3769336Sdfr	NFSERR_ACCES,
3779336Sdfr	NFSERR_EXIST,
3789336Sdfr	NFSERR_NOTDIR,
3799336Sdfr	NFSERR_INVAL,
3809336Sdfr	NFSERR_ROFS,
3819336Sdfr	NFSERR_NAMETOL,
3829336Sdfr	NFSERR_NOTEMPTY,
3839336Sdfr	NFSERR_STALE,
3849336Sdfr	NFSERR_BADHANDLE,
3859336Sdfr	NFSERR_NOTSUPP,
3869336Sdfr	NFSERR_SERVERFAULT,
3879336Sdfr	0,
3889336Sdfr};
3899336Sdfr
390129639Srwatsonstatic const short nfsv3err_rename[] = {
3919336Sdfr	NFSERR_IO,
3929336Sdfr	NFSERR_NOENT,
3939336Sdfr	NFSERR_IO,
3949336Sdfr	NFSERR_ACCES,
3959336Sdfr	NFSERR_EXIST,
3969336Sdfr	NFSERR_XDEV,
3979336Sdfr	NFSERR_NOTDIR,
3989336Sdfr	NFSERR_ISDIR,
3999336Sdfr	NFSERR_INVAL,
4009336Sdfr	NFSERR_NOSPC,
4019336Sdfr	NFSERR_ROFS,
4029336Sdfr	NFSERR_MLINK,
4039336Sdfr	NFSERR_NAMETOL,
4049336Sdfr	NFSERR_NOTEMPTY,
4059336Sdfr	NFSERR_DQUOT,
4069336Sdfr	NFSERR_STALE,
4079336Sdfr	NFSERR_BADHANDLE,
4089336Sdfr	NFSERR_NOTSUPP,
4099336Sdfr	NFSERR_SERVERFAULT,
4109336Sdfr	0,
4119336Sdfr};
4129336Sdfr
413129639Srwatsonstatic const short nfsv3err_link[] = {
4149336Sdfr	NFSERR_IO,
4159336Sdfr	NFSERR_IO,
4169336Sdfr	NFSERR_ACCES,
4179336Sdfr	NFSERR_EXIST,
4189336Sdfr	NFSERR_XDEV,
4199336Sdfr	NFSERR_NOTDIR,
4209336Sdfr	NFSERR_INVAL,
4219336Sdfr	NFSERR_NOSPC,
4229336Sdfr	NFSERR_ROFS,
4239336Sdfr	NFSERR_MLINK,
4249336Sdfr	NFSERR_NAMETOL,
4259336Sdfr	NFSERR_DQUOT,
4269336Sdfr	NFSERR_STALE,
4279336Sdfr	NFSERR_BADHANDLE,
4289336Sdfr	NFSERR_NOTSUPP,
4299336Sdfr	NFSERR_SERVERFAULT,
4309336Sdfr	0,
4319336Sdfr};
4329336Sdfr
433129639Srwatsonstatic const short nfsv3err_readdir[] = {
4349336Sdfr	NFSERR_IO,
4359336Sdfr	NFSERR_IO,
4369336Sdfr	NFSERR_ACCES,
4379336Sdfr	NFSERR_NOTDIR,
4389336Sdfr	NFSERR_STALE,
4399336Sdfr	NFSERR_BADHANDLE,
4409336Sdfr	NFSERR_BAD_COOKIE,
4419336Sdfr	NFSERR_TOOSMALL,
4429336Sdfr	NFSERR_SERVERFAULT,
4439336Sdfr	0,
4449336Sdfr};
4459336Sdfr
446129639Srwatsonstatic const short nfsv3err_readdirplus[] = {
4479336Sdfr	NFSERR_IO,
4489336Sdfr	NFSERR_IO,
4499336Sdfr	NFSERR_ACCES,
4509336Sdfr	NFSERR_NOTDIR,
4519336Sdfr	NFSERR_STALE,
4529336Sdfr	NFSERR_BADHANDLE,
4539336Sdfr	NFSERR_BAD_COOKIE,
4549336Sdfr	NFSERR_NOTSUPP,
4559336Sdfr	NFSERR_TOOSMALL,
4569336Sdfr	NFSERR_SERVERFAULT,
4579336Sdfr	0,
4589336Sdfr};
4599336Sdfr
460129639Srwatsonstatic const short nfsv3err_fsstat[] = {
4619336Sdfr	NFSERR_IO,
4629336Sdfr	NFSERR_IO,
4639336Sdfr	NFSERR_STALE,
4649336Sdfr	NFSERR_BADHANDLE,
4659336Sdfr	NFSERR_SERVERFAULT,
4669336Sdfr	0,
4679336Sdfr};
4689336Sdfr
469129639Srwatsonstatic const short nfsv3err_fsinfo[] = {
4709336Sdfr	NFSERR_STALE,
4719336Sdfr	NFSERR_STALE,
4729336Sdfr	NFSERR_BADHANDLE,
4739336Sdfr	NFSERR_SERVERFAULT,
4749336Sdfr	0,
4759336Sdfr};
4769336Sdfr
477129639Srwatsonstatic const short nfsv3err_pathconf[] = {
4789336Sdfr	NFSERR_STALE,
4799336Sdfr	NFSERR_STALE,
4809336Sdfr	NFSERR_BADHANDLE,
4819336Sdfr	NFSERR_SERVERFAULT,
4829336Sdfr	0,
4839336Sdfr};
4849336Sdfr
485129639Srwatsonstatic const short nfsv3err_commit[] = {
4869336Sdfr	NFSERR_IO,
4879336Sdfr	NFSERR_IO,
4889336Sdfr	NFSERR_STALE,
4899336Sdfr	NFSERR_BADHANDLE,
4909336Sdfr	NFSERR_SERVERFAULT,
4919336Sdfr	0,
4929336Sdfr};
4939336Sdfr
494129639Srwatsonstatic const short *nfsrv_v3errmap[] = {
4959336Sdfr	nfsv3err_null,
4969336Sdfr	nfsv3err_getattr,
4979336Sdfr	nfsv3err_setattr,
4989336Sdfr	nfsv3err_lookup,
4999336Sdfr	nfsv3err_access,
5009336Sdfr	nfsv3err_readlink,
5019336Sdfr	nfsv3err_read,
5029336Sdfr	nfsv3err_write,
5039336Sdfr	nfsv3err_create,
5049336Sdfr	nfsv3err_mkdir,
5059336Sdfr	nfsv3err_symlink,
5069336Sdfr	nfsv3err_mknod,
5079336Sdfr	nfsv3err_remove,
5089336Sdfr	nfsv3err_rmdir,
5099336Sdfr	nfsv3err_rename,
5109336Sdfr	nfsv3err_link,
5119336Sdfr	nfsv3err_readdir,
5129336Sdfr	nfsv3err_readdirplus,
5139336Sdfr	nfsv3err_fsstat,
5149336Sdfr	nfsv3err_fsinfo,
5159336Sdfr	nfsv3err_pathconf,
5169336Sdfr	nfsv3err_commit,
5179336Sdfr};
5189336Sdfr
5191541Srgrimes/*
5201541Srgrimes * Called once to initialize data structures...
5211541Srgrimes */
52283651Speterstatic int
52383700Speternfsrv_modevent(module_t mod, int type, void *data)
5241541Srgrimes{
525132199Sphk	int error = 0;
5261541Srgrimes
527129639Srwatson	NET_LOCK_GIANT();
52883700Speter	switch (type) {
52983700Speter	case MOD_LOAD:
530129639Srwatson		mtx_init(&nfsd_mtx, "nfsd_mtx", NULL, MTX_DEF);
53189094Smsmith		nfsrv_rpc_vers = txdr_unsigned(RPC_VER2);
53289094Smsmith		nfsrv_rpc_call = txdr_unsigned(RPC_CALL);
53389094Smsmith		nfsrv_rpc_reply = txdr_unsigned(RPC_REPLY);
53489094Smsmith		nfsrv_rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
53589094Smsmith		nfsrv_rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
53689094Smsmith		nfsrv_rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
53789094Smsmith		nfsrv_rpc_autherr = txdr_unsigned(RPC_AUTHERR);
53889094Smsmith		nfsrv_rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
53989094Smsmith		nfsrv_nfs_prog = txdr_unsigned(NFS_PROG);
54089094Smsmith		nfsrv_nfs_true = txdr_unsigned(TRUE);
54189094Smsmith		nfsrv_nfs_false = txdr_unsigned(FALSE);
54289094Smsmith		nfsrv_nfs_xdrneg1 = txdr_unsigned(-1);
54389094Smsmith		nfsrv_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
54489094Smsmith		if (nfsrv_ticks < 1)
54589094Smsmith			nfsrv_ticks = 1;
54683651Speter
547129639Srwatson		nfsrv_initcache();	/* Init the server request cache */
548129639Srwatson		NFSD_LOCK();
54983700Speter		nfsrv_init(0);		/* Init server data structures */
550132591Srwatson		if (debug_mpsafenet)
551132591Srwatson			callout_init(&nfsrv_callout, CALLOUT_MPSAFE);
552132591Srwatson		else
553132591Srwatson			callout_init(&nfsrv_callout, 0);
554129639Srwatson		NFSD_UNLOCK();
55583700Speter		nfsrv_timer(0);
5561541Srgrimes
55783700Speter		nfs_prev_nfssvc_sy_narg = sysent[SYS_nfssvc].sy_narg;
558129888Srwatson		sysent[SYS_nfssvc].sy_narg = 2 | SYF_MPSAFE;
55983700Speter		nfs_prev_nfssvc_sy_call = sysent[SYS_nfssvc].sy_call;
56083700Speter		sysent[SYS_nfssvc].sy_call = (sy_call_t *)nfssvc;
56183700Speter		break;
5622997Swollman
563132199Sphk	case MOD_UNLOAD:
564129902Sbmilekic		if (nfsrv_numnfsd != 0) {
565132199Sphk			error = EBUSY;
566132199Sphk			break;
567129902Sbmilekic		}
56883700Speter
569126723Skan		callout_stop(&nfsrv_callout);
57083700Speter		sysent[SYS_nfssvc].sy_narg = nfs_prev_nfssvc_sy_narg;
57183700Speter		sysent[SYS_nfssvc].sy_call = nfs_prev_nfssvc_sy_call;
572129639Srwatson		mtx_destroy(&nfsd_mtx);
57383700Speter		break;
574132199Sphk	default:
575132199Sphk		error = EOPNOTSUPP;
576132199Sphk		break;
57783700Speter	}
578129639Srwatson	NET_UNLOCK_GIANT();
579132199Sphk	return error;
5801541Srgrimes}
58183700Speterstatic moduledata_t nfsserver_mod = {
58283700Speter	"nfsserver",
58383700Speter	nfsrv_modevent,
58483700Speter	NULL,
58583700Speter};
58683700SpeterDECLARE_MODULE(nfsserver, nfsserver_mod, SI_SUB_VFS, SI_ORDER_ANY);
5871541Srgrimes
58883700Speter/* So that loader and kldload(2) can find us, wherever we are.. */
58983700SpeterMODULE_VERSION(nfsserver, 1);
59038894Sbde
5911541Srgrimes/*
59227446Sdfr * Set up nameidata for a lookup() call and do it.
59327446Sdfr *
59427446Sdfr * If pubflag is set, this call is done for a lookup operation on the
59527446Sdfr * public filehandle. In that case we allow crossing mountpoints and
59627446Sdfr * absolute pathnames. However, the caller is expected to check that
59727446Sdfr * the lookup result is within the public fs, and deny access if
59827446Sdfr * it is not.
59948125Sjulian *
60048125Sjulian * nfs_namei() clears out garbage fields that namei() might leave garbage.
60148125Sjulian * This is mainly ni_vp and ni_dvp when an error occurs, and ni_dvp when no
60248125Sjulian * error occurs but the parent was not requested.
60348125Sjulian *
60483651Speter * dirp may be set whether an error is returned or not, and must be
60548125Sjulian * released by the caller.
6061541Srgrimes */
6071549Srgrimesint
60883651Speternfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
60983651Speter    struct nfssvc_sock *slp, struct sockaddr *nam, struct mbuf **mdp,
610115301Struckman    caddr_t *dposp, struct vnode **retdirp, int v3, struct vattr *retdirattrp,
611115301Struckman    int *retdirattr_retp, struct thread *td, int pubflag)
6121541Srgrimes{
61383651Speter	int i, rem;
61483651Speter	struct mbuf *md;
61583651Speter	char *fromcp, *tocp, *cp;
61627446Sdfr	struct iovec aiov;
61727446Sdfr	struct uio auio;
6181541Srgrimes	struct vnode *dp;
61927446Sdfr	int error, rdonly, linklen;
6201541Srgrimes	struct componentname *cnp = &ndp->ni_cnd;
621115301Struckman	int lockleaf = (cnp->cn_flags & LOCKLEAF) != 0;
6221541Srgrimes
623129639Srwatson	NFSD_LOCK_ASSERT();
624129639Srwatson	NFSD_UNLOCK();
625129639Srwatson	mtx_lock(&Giant);	/* VFS */
626129639Srwatson
62799797Sdillon	*retdirp = NULL;
628105481Srwatson	cnp->cn_flags |= NOMACCHECK;
629111119Simp	cnp->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
63029653Sdyson
6311541Srgrimes	/*
6321541Srgrimes	 * Copy the name from the mbuf list to ndp->ni_pnbuf
6331541Srgrimes	 * and set the various ndp fields appropriately.
6341541Srgrimes	 */
6351541Srgrimes	fromcp = *dposp;
6361541Srgrimes	tocp = cnp->cn_pnbuf;
6371541Srgrimes	md = *mdp;
6381541Srgrimes	rem = mtod(md, caddr_t) + md->m_len - fromcp;
6391541Srgrimes	for (i = 0; i < len; i++) {
6401541Srgrimes		while (rem == 0) {
6411541Srgrimes			md = md->m_next;
6421541Srgrimes			if (md == NULL) {
6431541Srgrimes				error = EBADRPC;
6441541Srgrimes				goto out;
6451541Srgrimes			}
6461541Srgrimes			fromcp = mtod(md, caddr_t);
6471541Srgrimes			rem = md->m_len;
6481541Srgrimes		}
64927446Sdfr		if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) {
6509336Sdfr			error = EACCES;
6511541Srgrimes			goto out;
6521541Srgrimes		}
6531541Srgrimes		*tocp++ = *fromcp++;
6541541Srgrimes		rem--;
6551541Srgrimes	}
6561541Srgrimes	*tocp = '\0';
6571541Srgrimes	*mdp = md;
6581541Srgrimes	*dposp = fromcp;
6591541Srgrimes	len = nfsm_rndup(len)-len;
6601541Srgrimes	if (len > 0) {
6611541Srgrimes		if (rem >= len)
6621541Srgrimes			*dposp += len;
66327609Sdfr		else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
6649336Sdfr			goto out;
6651541Srgrimes	}
66627446Sdfr
6671541Srgrimes	/*
6681541Srgrimes	 * Extract and set starting directory.
6691541Srgrimes	 */
670129639Srwatson	mtx_unlock(&Giant);	/* VFS */
671129639Srwatson	NFSD_LOCK();
67227446Sdfr	error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
67383651Speter	    nam, &rdonly, pubflag);
674129639Srwatson	NFSD_UNLOCK();
675129639Srwatson	mtx_lock(&Giant);	/* VFS */
67627446Sdfr	if (error)
6771541Srgrimes		goto out;
6781541Srgrimes	if (dp->v_type != VDIR) {
67917761Sdyson		vrele(dp);
6801541Srgrimes		error = ENOTDIR;
6811541Srgrimes		goto out;
6821541Srgrimes	}
68327446Sdfr
68427446Sdfr	if (rdonly)
68527446Sdfr		cnp->cn_flags |= RDONLY;
68627446Sdfr
68748125Sjulian	/*
68883651Speter	 * Set return directory.  Reference to dp is implicitly transfered
68948125Sjulian	 * to the returned pointer
69048125Sjulian	 */
69127609Sdfr	*retdirp = dp;
692115301Struckman	if (v3) {
693115301Struckman		vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, td);
694115301Struckman		*retdirattr_retp = VOP_GETATTR(dp, retdirattrp,
695115301Struckman			ndp->ni_cnd.cn_cred, td);
696115301Struckman		VOP_UNLOCK(dp, 0, td);
697115301Struckman	}
69827609Sdfr
69927446Sdfr	if (pubflag) {
70027446Sdfr		/*
70127446Sdfr		 * Oh joy. For WebNFS, handle those pesky '%' escapes,
70227446Sdfr		 * and the 'native path' indicator.
70327446Sdfr		 */
704111119Simp		cp = uma_zalloc(namei_zone, M_WAITOK);
70527446Sdfr		fromcp = cnp->cn_pnbuf;
70627446Sdfr		tocp = cp;
70727446Sdfr		if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) {
70827446Sdfr			switch ((unsigned char)*fromcp) {
70927446Sdfr			case WEBNFS_NATIVE_CHAR:
71027446Sdfr				/*
71127446Sdfr				 * 'Native' path for us is the same
71227446Sdfr				 * as a path according to the NFS spec,
71327446Sdfr				 * just skip the escape char.
71427446Sdfr				 */
71527446Sdfr				fromcp++;
71627446Sdfr				break;
71727446Sdfr			/*
71827446Sdfr			 * More may be added in the future, range 0x80-0xff
71927446Sdfr			 */
72027446Sdfr			default:
72127446Sdfr				error = EIO;
72292783Sjeff				uma_zfree(namei_zone, cp);
72327446Sdfr				goto out;
72427446Sdfr			}
72527446Sdfr		}
72627446Sdfr		/*
72727446Sdfr		 * Translate the '%' escapes, URL-style.
72827446Sdfr		 */
72927446Sdfr		while (*fromcp != '\0') {
73027446Sdfr			if (*fromcp == WEBNFS_ESC_CHAR) {
73127446Sdfr				if (fromcp[1] != '\0' && fromcp[2] != '\0') {
73227446Sdfr					fromcp++;
73327446Sdfr					*tocp++ = HEXSTRTOI(fromcp);
73427446Sdfr					fromcp += 2;
73527446Sdfr					continue;
73627446Sdfr				} else {
73727446Sdfr					error = ENOENT;
73892783Sjeff					uma_zfree(namei_zone, cp);
73927446Sdfr					goto out;
74027446Sdfr				}
74127446Sdfr			} else
74227446Sdfr				*tocp++ = *fromcp++;
74327446Sdfr		}
74427446Sdfr		*tocp = '\0';
74592783Sjeff		uma_zfree(namei_zone, cnp->cn_pnbuf);
74627446Sdfr		cnp->cn_pnbuf = cp;
74727446Sdfr	}
74827446Sdfr
74927446Sdfr	ndp->ni_pathlen = (tocp - cnp->cn_pnbuf) + 1;
75027446Sdfr	ndp->ni_segflg = UIO_SYSSPACE;
75127446Sdfr
75227446Sdfr	if (pubflag) {
75327446Sdfr		ndp->ni_rootdir = rootvnode;
75427446Sdfr		ndp->ni_loopcnt = 0;
75527446Sdfr		if (cnp->cn_pnbuf[0] == '/')
75627446Sdfr			dp = rootvnode;
75727446Sdfr	} else {
75827609Sdfr		cnp->cn_flags |= NOCROSSMOUNT;
75927446Sdfr	}
76027446Sdfr
76148125Sjulian	/*
76248125Sjulian	 * Initialize for scan, set ni_startdir and bump ref on dp again
763123608Sjhb	 * because lookup() will dereference ni_startdir.
76448125Sjulian	 */
76548125Sjulian
76683366Sjulian	cnp->cn_thread = td;
7679336Sdfr	VREF(dp);
76848125Sjulian	ndp->ni_startdir = dp;
76927446Sdfr
770115301Struckman	if (!lockleaf)
771115301Struckman		cnp->cn_flags |= LOCKLEAF;
77248125Sjulian	for (;;) {
77348125Sjulian		cnp->cn_nameptr = cnp->cn_pnbuf;
77448125Sjulian		/*
77548125Sjulian		 * Call lookup() to do the real work.  If an error occurs,
77648125Sjulian		 * ndp->ni_vp and ni_dvp are left uninitialized or NULL and
77748125Sjulian		 * we do not have to dereference anything before returning.
77848125Sjulian		 * In either case ni_startdir will be dereferenced and NULLed
77948125Sjulian		 * out.
78048125Sjulian		 */
78148125Sjulian		error = lookup(ndp);
78248125Sjulian		if (error)
78348125Sjulian			break;
78448125Sjulian
78548125Sjulian		/*
78683651Speter		 * Check for encountering a symbolic link.  Trivial
78748125Sjulian		 * termination occurs if no symlink encountered.
78848125Sjulian		 * Note: zfree is safe because error is 0, so we will
78948125Sjulian		 * not zfree it again when we break.
79048125Sjulian		 */
79148125Sjulian		if ((cnp->cn_flags & ISSYMLINK) == 0) {
79248125Sjulian			if (cnp->cn_flags & (SAVENAME | SAVESTART))
79348125Sjulian				cnp->cn_flags |= HASBUF;
79448125Sjulian			else
79592783Sjeff				uma_zfree(namei_zone, cnp->cn_pnbuf);
796115301Struckman			if (ndp->ni_vp && !lockleaf)
797115301Struckman				VOP_UNLOCK(ndp->ni_vp, 0, td);
79848125Sjulian			break;
79927446Sdfr		}
80048125Sjulian
80148125Sjulian		/*
80248125Sjulian		 * Validate symlink
80348125Sjulian		 */
8041541Srgrimes		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
80583366Sjulian			VOP_UNLOCK(ndp->ni_dvp, 0, td);
80627446Sdfr		if (!pubflag) {
80727446Sdfr			error = EINVAL;
80848125Sjulian			goto badlink2;
80927446Sdfr		}
81027446Sdfr
81127446Sdfr		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
81227446Sdfr			error = ELOOP;
81348125Sjulian			goto badlink2;
81427446Sdfr		}
81527609Sdfr		if (ndp->ni_pathlen > 1)
816111119Simp			cp = uma_zalloc(namei_zone, M_WAITOK);
8171541Srgrimes		else
81827446Sdfr			cp = cnp->cn_pnbuf;
81927446Sdfr		aiov.iov_base = cp;
82027446Sdfr		aiov.iov_len = MAXPATHLEN;
82127446Sdfr		auio.uio_iov = &aiov;
82227446Sdfr		auio.uio_iovcnt = 1;
82327446Sdfr		auio.uio_offset = 0;
82427446Sdfr		auio.uio_rw = UIO_READ;
82527446Sdfr		auio.uio_segflg = UIO_SYSSPACE;
82699797Sdillon		auio.uio_td = NULL;
82727446Sdfr		auio.uio_resid = MAXPATHLEN;
82827446Sdfr		error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
82927446Sdfr		if (error) {
83048125Sjulian		badlink1:
83127446Sdfr			if (ndp->ni_pathlen > 1)
83292783Sjeff				uma_zfree(namei_zone, cp);
83348125Sjulian		badlink2:
834155160Sjeff			vput(ndp->ni_vp);
83548125Sjulian			vrele(ndp->ni_dvp);
83627446Sdfr			break;
83727446Sdfr		}
83827446Sdfr		linklen = MAXPATHLEN - auio.uio_resid;
83927446Sdfr		if (linklen == 0) {
84027446Sdfr			error = ENOENT;
84148125Sjulian			goto badlink1;
84227446Sdfr		}
84327446Sdfr		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
84427446Sdfr			error = ENAMETOOLONG;
84548125Sjulian			goto badlink1;
84627446Sdfr		}
84748125Sjulian
84848125Sjulian		/*
84948125Sjulian		 * Adjust or replace path
85048125Sjulian		 */
85127446Sdfr		if (ndp->ni_pathlen > 1) {
85227446Sdfr			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
85392783Sjeff			uma_zfree(namei_zone, cnp->cn_pnbuf);
85427446Sdfr			cnp->cn_pnbuf = cp;
85527446Sdfr		} else
85627446Sdfr			cnp->cn_pnbuf[linklen] = '\0';
85727446Sdfr		ndp->ni_pathlen += linklen;
85848125Sjulian
85927446Sdfr		/*
86083651Speter		 * Cleanup refs for next loop and check if root directory
86183651Speter		 * should replace current directory.  Normally ni_dvp
86248125Sjulian		 * becomes the new base directory and is cleaned up when
86348125Sjulian		 * we loop.  Explicitly null pointers after invalidation
86448125Sjulian		 * to clarify operation.
86527446Sdfr		 */
86648125Sjulian		vput(ndp->ni_vp);
86748125Sjulian		ndp->ni_vp = NULL;
86848125Sjulian
86927446Sdfr		if (cnp->cn_pnbuf[0] == '/') {
87048125Sjulian			vrele(ndp->ni_dvp);
87148125Sjulian			ndp->ni_dvp = ndp->ni_rootdir;
87248125Sjulian			VREF(ndp->ni_dvp);
87327446Sdfr		}
87448125Sjulian		ndp->ni_startdir = ndp->ni_dvp;
87548125Sjulian		ndp->ni_dvp = NULL;
8761541Srgrimes	}
877115301Struckman	if (!lockleaf)
878115301Struckman		cnp->cn_flags &= ~LOCKLEAF;
879159268Skib	if (cnp->cn_flags & GIANTHELD) {
880159268Skib		mtx_unlock(&Giant);
881159268Skib		cnp->cn_flags &= ~GIANTHELD;
882159268Skib	}
88348125Sjulian
88448125Sjulian	/*
88548125Sjulian	 * nfs_namei() guarentees that fields will not contain garbage
88648125Sjulian	 * whether an error occurs or not.  This allows the caller to track
88748125Sjulian	 * cleanup state trivially.
88848125Sjulian	 */
8891541Srgrimesout:
89048125Sjulian	if (error) {
89192783Sjeff		uma_zfree(namei_zone, cnp->cn_pnbuf);
89248125Sjulian		ndp->ni_vp = NULL;
89348125Sjulian		ndp->ni_dvp = NULL;
89448125Sjulian		ndp->ni_startdir = NULL;
89548125Sjulian		cnp->cn_flags &= ~HASBUF;
89648125Sjulian	} else if ((ndp->ni_cnd.cn_flags & (WANTPARENT|LOCKPARENT)) == 0) {
89748125Sjulian		ndp->ni_dvp = NULL;
89848125Sjulian	}
899129639Srwatson	mtx_unlock(&Giant);	/* VFS */
900129639Srwatson	NFSD_LOCK();
9011541Srgrimes	return (error);
9021541Srgrimes}
9031541Srgrimes
9041541Srgrimes/*
9051541Srgrimes * A fiddled version of m_adj() that ensures null fill to a long
9061541Srgrimes * boundary and only trims off the back end
9071541Srgrimes */
9081541Srgrimesvoid
90983651Speternfsm_adj(struct mbuf *mp, int len, int nul)
9101541Srgrimes{
91183651Speter	struct mbuf *m;
91283651Speter	int count, i;
91383651Speter	char *cp;
9141541Srgrimes
915129639Srwatson	NFSD_LOCK_DONTCARE();
916129639Srwatson
9171541Srgrimes	/*
9181541Srgrimes	 * Trim from tail.  Scan the mbuf chain,
9191541Srgrimes	 * calculating its length and finding the last mbuf.
9201541Srgrimes	 * If the adjustment only affects this mbuf, then just
9211541Srgrimes	 * adjust and return.  Otherwise, rescan and truncate
9221541Srgrimes	 * after the remaining size.
9231541Srgrimes	 */
9241541Srgrimes	count = 0;
9251541Srgrimes	m = mp;
9261541Srgrimes	for (;;) {
9271541Srgrimes		count += m->m_len;
92899797Sdillon		if (m->m_next == NULL)
9291541Srgrimes			break;
9301541Srgrimes		m = m->m_next;
9311541Srgrimes	}
9321541Srgrimes	if (m->m_len > len) {
9331541Srgrimes		m->m_len -= len;
9341541Srgrimes		if (nul > 0) {
9351541Srgrimes			cp = mtod(m, caddr_t)+m->m_len-nul;
9361541Srgrimes			for (i = 0; i < nul; i++)
9371541Srgrimes				*cp++ = '\0';
9381541Srgrimes		}
9391541Srgrimes		return;
9401541Srgrimes	}
9411541Srgrimes	count -= len;
9421541Srgrimes	if (count < 0)
9431541Srgrimes		count = 0;
9441541Srgrimes	/*
9451541Srgrimes	 * Correct length for chain is "count".
9461541Srgrimes	 * Find the mbuf with last data, adjust its length,
9471541Srgrimes	 * and toss data from remaining mbufs on chain.
9481541Srgrimes	 */
9491541Srgrimes	for (m = mp; m; m = m->m_next) {
9501541Srgrimes		if (m->m_len >= count) {
9511541Srgrimes			m->m_len = count;
9521541Srgrimes			if (nul > 0) {
9531541Srgrimes				cp = mtod(m, caddr_t)+m->m_len-nul;
9541541Srgrimes				for (i = 0; i < nul; i++)
9551541Srgrimes					*cp++ = '\0';
9561541Srgrimes			}
957144246Ssam			if (m->m_next != NULL) {
958144246Ssam				m_freem(m->m_next);
959144246Ssam				m->m_next = NULL;
960144246Ssam			}
9611541Srgrimes			break;
9621541Srgrimes		}
9631541Srgrimes		count -= m->m_len;
9641541Srgrimes	}
9651541Srgrimes}
9661541Srgrimes
9671541Srgrimes/*
9689336Sdfr * Make these functions instead of macros, so that the kernel text size
9699336Sdfr * doesn't get too big...
9709336Sdfr */
9719336Sdfrvoid
97283651Speternfsm_srvwcc(struct nfsrv_descript *nfsd, int before_ret,
97383651Speter    struct vattr *before_vap, int after_ret, struct vattr *after_vap,
97483651Speter    struct mbuf **mbp, char **bposp)
9759336Sdfr{
97683651Speter	struct mbuf *mb = *mbp;
97783651Speter	char *bpos = *bposp;
97883651Speter	u_int32_t *tl;
9799336Sdfr
9809336Sdfr	if (before_ret) {
98184002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
98289094Smsmith		*tl = nfsrv_nfs_false;
9839336Sdfr	} else {
98484002Speter		tl = nfsm_build(u_int32_t *, 7 * NFSX_UNSIGNED);
98589094Smsmith		*tl++ = nfsrv_nfs_true;
98647751Speter		txdr_hyper(before_vap->va_size, tl);
9879336Sdfr		tl += 2;
9889336Sdfr		txdr_nfsv3time(&(before_vap->va_mtime), tl);
9899336Sdfr		tl += 2;
9909336Sdfr		txdr_nfsv3time(&(before_vap->va_ctime), tl);
9919336Sdfr	}
9929336Sdfr	*bposp = bpos;
9939336Sdfr	*mbp = mb;
9949336Sdfr	nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp);
9959336Sdfr}
9969336Sdfr
9979336Sdfrvoid
99883651Speternfsm_srvpostopattr(struct nfsrv_descript *nfsd, int after_ret,
99983651Speter    struct vattr *after_vap, struct mbuf **mbp, char **bposp)
10009336Sdfr{
100183651Speter	struct mbuf *mb = *mbp;
100283651Speter	char *bpos = *bposp;
100383651Speter	u_int32_t *tl;
100483651Speter	struct nfs_fattr *fp;
10059336Sdfr
10069336Sdfr	if (after_ret) {
100784002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
100889094Smsmith		*tl = nfsrv_nfs_false;
10099336Sdfr	} else {
101084002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED + NFSX_V3FATTR);
101189094Smsmith		*tl++ = nfsrv_nfs_true;
10129336Sdfr		fp = (struct nfs_fattr *)tl;
10139336Sdfr		nfsm_srvfattr(nfsd, after_vap, fp);
10149336Sdfr	}
10159336Sdfr	*mbp = mb;
10169336Sdfr	*bposp = bpos;
10179336Sdfr}
10189336Sdfr
10199336Sdfrvoid
102083651Speternfsm_srvfattr(struct nfsrv_descript *nfsd, struct vattr *vap,
102183651Speter    struct nfs_fattr *fp)
10229336Sdfr{
10239336Sdfr
10249336Sdfr	fp->fa_nlink = txdr_unsigned(vap->va_nlink);
10259336Sdfr	fp->fa_uid = txdr_unsigned(vap->va_uid);
10269336Sdfr	fp->fa_gid = txdr_unsigned(vap->va_gid);
10279336Sdfr	if (nfsd->nd_flag & ND_NFSV3) {
10289336Sdfr		fp->fa_type = vtonfsv3_type(vap->va_type);
10299336Sdfr		fp->fa_mode = vtonfsv3_mode(vap->va_mode);
103047751Speter		txdr_hyper(vap->va_size, &fp->fa3_size);
103147751Speter		txdr_hyper(vap->va_bytes, &fp->fa3_used);
103247028Sphk		fp->fa3_rdev.specdata1 = txdr_unsigned(umajor(vap->va_rdev));
103347028Sphk		fp->fa3_rdev.specdata2 = txdr_unsigned(uminor(vap->va_rdev));
10349336Sdfr		fp->fa3_fsid.nfsuquad[0] = 0;
10359336Sdfr		fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
10369336Sdfr		fp->fa3_fileid.nfsuquad[0] = 0;
10379336Sdfr		fp->fa3_fileid.nfsuquad[1] = txdr_unsigned(vap->va_fileid);
10389336Sdfr		txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
10399336Sdfr		txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
10409336Sdfr		txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
10419336Sdfr	} else {
10429336Sdfr		fp->fa_type = vtonfsv2_type(vap->va_type);
10439336Sdfr		fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
10449336Sdfr		fp->fa2_size = txdr_unsigned(vap->va_size);
10459336Sdfr		fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
10469336Sdfr		if (vap->va_type == VFIFO)
10479336Sdfr			fp->fa2_rdev = 0xffffffff;
10489336Sdfr		else
10499336Sdfr			fp->fa2_rdev = txdr_unsigned(vap->va_rdev);
10509336Sdfr		fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
10519336Sdfr		fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
10529336Sdfr		fp->fa2_fileid = txdr_unsigned(vap->va_fileid);
10539336Sdfr		txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
10549336Sdfr		txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
10559336Sdfr		txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
10569336Sdfr	}
10579336Sdfr}
10589336Sdfr
10599336Sdfr/*
10601541Srgrimes * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
10611541Srgrimes * 	- look up fsid in mount list (if not found ret error)
10621541Srgrimes *	- get vp and export rights by calling VFS_FHTOVP()
10631541Srgrimes *	- if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
10641541Srgrimes *	- if not lockflag unlock it with VOP_UNLOCK()
10651541Srgrimes */
10661549Srgrimesint
106783651Speternfsrv_fhtovp(fhandle_t *fhp, int lockflag, struct vnode **vpp,
106883651Speter    struct ucred *cred, struct nfssvc_sock *slp, struct sockaddr *nam,
106983651Speter    int *rdonlyp, int pubflag)
10701541Srgrimes{
107183366Sjulian	struct thread *td = curthread; /* XXX */
107283651Speter	struct mount *mp;
107383651Speter	int i;
10741541Srgrimes	struct ucred *credanon;
10751541Srgrimes	int error, exflags;
1076157325Sjeff	int vfslocked;
107736534Speter#ifdef MNT_EXNORESPORT		/* XXX needs mountd and /etc/exports help yet */
107836534Speter	struct sockaddr_int *saddr;
107936534Speter#endif
10801541Srgrimes
1081129639Srwatson	NFSD_LOCK_ASSERT();
1082129639Srwatson
108399797Sdillon	*vpp = NULL;
108427446Sdfr
108527446Sdfr	if (nfs_ispublicfh(fhp)) {
108627446Sdfr		if (!pubflag || !nfs_pub.np_valid)
108727446Sdfr			return (ESTALE);
108827446Sdfr		fhp = &nfs_pub.np_handle;
108927446Sdfr	}
109027446Sdfr
109122521Sdyson	mp = vfs_getvfs(&fhp->fh_fsid);
10923305Sphk	if (!mp)
10931541Srgrimes		return (ESTALE);
1094129639Srwatson	NFSD_UNLOCK();
1095157325Sjeff	vfslocked = VFS_LOCK_GIANT(mp);
109651138Salfred	error = VFS_CHECKEXP(mp, nam, &exflags, &credanon);
10973305Sphk	if (error)
1098129639Srwatson		goto out;
109951138Salfred	error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp);
110051138Salfred	if (error)
1101129639Srwatson		goto out;
110236534Speter#ifdef MNT_EXNORESPORT
110336534Speter	if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) {
110436534Speter		saddr = (struct sockaddr_in *)nam;
1105100134Salfred		if ((saddr->sin_family == AF_INET ||
1106100134Salfred		     saddr->sin_family == AF_INET6) &&
1107100134Salfred	/* same code for INET and INET6: sin*_port at same offet */
110836534Speter		    ntohs(saddr->sin_port) >= IPPORT_RESERVED) {
110936534Speter			vput(*vpp);
111054485Sdillon			*vpp = NULL;
1111129639Srwatson			error = NFSERR_AUTHERR | AUTH_TOOWEAK;
111236534Speter		}
111336534Speter	}
111436534Speter#endif
11151541Srgrimes	/*
11161541Srgrimes	 * Check/setup credentials.
11171541Srgrimes	 */
111883651Speter	if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
11191541Srgrimes		cred->cr_uid = credanon->cr_uid;
11201541Srgrimes		for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
11211541Srgrimes			cred->cr_groups[i] = credanon->cr_groups[i];
11223664Sphk		cred->cr_ngroups = i;
11231541Srgrimes	}
11241541Srgrimes	if (exflags & MNT_EXRDONLY)
11251541Srgrimes		*rdonlyp = 1;
11261541Srgrimes	else
11271541Srgrimes		*rdonlyp = 0;
11287969Sdyson
11291541Srgrimes	if (!lockflag)
113083366Sjulian		VOP_UNLOCK(*vpp, 0, td);
1131129639Srwatsonout:
1132157325Sjeff	vfs_rel(mp);
1133157325Sjeff	VFS_UNLOCK_GIANT(vfslocked);
1134129639Srwatson	NFSD_LOCK();
1135129639Srwatson	return (error);
11361541Srgrimes}
11371541Srgrimes
113827446Sdfr
113927446Sdfr/*
114027446Sdfr * WebNFS: check if a filehandle is a public filehandle. For v3, this
114127446Sdfr * means a length of 0, for v2 it means all zeroes. nfsm_srvmtofh has
114227446Sdfr * transformed this to all zeroes in both cases, so check for it.
114327446Sdfr */
114427446Sdfrint
114583651Speternfs_ispublicfh(fhandle_t *fhp)
114627446Sdfr{
114727446Sdfr	char *cp = (char *)fhp;
114827446Sdfr	int i;
114927446Sdfr
1150129639Srwatson	NFSD_LOCK_DONTCARE();
1151129639Srwatson
115227446Sdfr	for (i = 0; i < NFSX_V3FH; i++)
115327446Sdfr		if (*cp++ != 0)
115427446Sdfr			return (FALSE);
115527446Sdfr	return (TRUE);
115627446Sdfr}
115783651Speter
11581541Srgrimes/*
11591541Srgrimes * This function compares two net addresses by family and returns TRUE
11601541Srgrimes * if they are the same host.
11611541Srgrimes * If there is any doubt, return FALSE.
11621541Srgrimes * The AF_INET family is handled as a special case so that address mbufs
11631541Srgrimes * don't need to be saved to store "struct in_addr", which is only 4 bytes.
11641541Srgrimes */
11651549Srgrimesint
116683651Speternetaddr_match(int family, union nethostaddr *haddr, struct sockaddr *nam)
11671541Srgrimes{
116883651Speter	struct sockaddr_in *inetaddr;
11691541Srgrimes
1170129639Srwatson	NFSD_LOCK_DONTCARE();
1171129639Srwatson
11721541Srgrimes	switch (family) {
11731541Srgrimes	case AF_INET:
117428270Swollman		inetaddr = (struct sockaddr_in *)nam;
11751541Srgrimes		if (inetaddr->sin_family == AF_INET &&
11761541Srgrimes		    inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
11771541Srgrimes			return (1);
11781541Srgrimes		break;
1179100134Salfred#ifdef INET6
1180100134Salfred	case AF_INET6:
1181100134Salfred	{
1182100134Salfred		register struct sockaddr_in6 *inet6addr1, *inet6addr2;
1183100134Salfred
1184100134Salfred		inet6addr1 = (struct sockaddr_in6 *)nam;
1185100134Salfred		inet6addr2 = (struct sockaddr_in6 *)haddr->had_nam;
1186100134Salfred	/* XXX - should test sin6_scope_id ? */
1187100134Salfred		if (inet6addr1->sin6_family == AF_INET6 &&
1188100134Salfred		    IN6_ARE_ADDR_EQUAL(&inet6addr1->sin6_addr,
1189100134Salfred				       &inet6addr2->sin6_addr))
1190100134Salfred			return (1);
1191100134Salfred		break;
1192100134Salfred	}
1193100134Salfred#endif
11941541Srgrimes	default:
11951541Srgrimes		break;
11961541Srgrimes	};
11971541Srgrimes	return (0);
11981541Srgrimes}
11995455Sdg
12009336Sdfr/*
12019336Sdfr * Map errnos to NFS error numbers. For Version 3 also filter out error
12029336Sdfr * numbers not specified for the associated procedure.
12039336Sdfr */
12045455Sdgint
120583651Speternfsrv_errmap(struct nfsrv_descript *nd, int err)
12069336Sdfr{
1207129639Srwatson	const short *defaulterrp, *errp;
1208102236Sphk	int e;
12099336Sdfr
1210129639Srwatson	NFSD_LOCK_DONTCARE();
1211129639Srwatson
12129336Sdfr	if (nd->nd_flag & ND_NFSV3) {
12139336Sdfr	    if (nd->nd_procnum <= NFSPROC_COMMIT) {
12149336Sdfr		errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
12159336Sdfr		while (*++errp) {
12169336Sdfr			if (*errp == err)
12179336Sdfr				return (err);
12189336Sdfr			else if (*errp > err)
12199336Sdfr				break;
12209336Sdfr		}
12219336Sdfr		return ((int)*defaulterrp);
12229336Sdfr	    } else
12239336Sdfr		return (err & 0xffff);
12249336Sdfr	}
1225102236Sphk	e = 0;
12269336Sdfr	if (err <= ELAST)
1227102236Sphk		e = nfsrv_v2errmap[err - 1];
1228102236Sphk	if (e != 0)
1229102236Sphk		return (e);
12309336Sdfr	return (NFSERR_IO);
12319336Sdfr}
12329336Sdfr
123336503Speter/*
123436503Speter * Sort the group list in increasing numerical order.
123536503Speter * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
123636503Speter *  that used to be here.)
123736503Speter */
123836503Spetervoid
123983651Speternfsrvw_sort(gid_t *list, int num)
124036503Speter{
124183651Speter	int i, j;
124236503Speter	gid_t v;
124336503Speter
1244129639Srwatson	NFSD_LOCK_DONTCARE();
1245129639Srwatson
124636503Speter	/* Insertion sort. */
124736503Speter	for (i = 1; i < num; i++) {
124836503Speter		v = list[i];
124936503Speter		/* find correct slot for value v, moving others up */
125036503Speter		for (j = i; --j >= 0 && v < list[j];)
125136503Speter			list[j + 1] = list[j];
125236503Speter		list[j + 1] = v;
125336503Speter	}
125436503Speter}
125536503Speter
125636503Speter/*
125736503Speter * copy credentials making sure that the result can be compared with bcmp().
125836503Speter */
125936503Spetervoid
126083651Speternfsrv_setcred(struct ucred *incred, struct ucred *outcred)
126136503Speter{
126283651Speter	int i;
126336503Speter
1264129639Srwatson	NFSD_LOCK_DONTCARE();
1265129639Srwatson
126636503Speter	bzero((caddr_t)outcred, sizeof (struct ucred));
1267150634Sjhb	refcount_init(&outcred->cr_ref, 1);
126836503Speter	outcred->cr_uid = incred->cr_uid;
126936503Speter	outcred->cr_ngroups = incred->cr_ngroups;
127036503Speter	for (i = 0; i < incred->cr_ngroups; i++)
127136503Speter		outcred->cr_groups[i] = incred->cr_groups[i];
127236503Speter	nfsrvw_sort(outcred->cr_groups, outcred->cr_ngroups);
127336503Speter}
127483651Speter
127583651Speter/*
127683651Speter * Helper functions for macros.
127783651Speter */
127883651Speter
127983651Spetervoid
128088091Siedowsenfsm_srvfhtom_xx(fhandle_t *f, int v3, struct mbuf **mb, caddr_t *bpos)
128183651Speter{
128288091Siedowse	u_int32_t *tl;
128383651Speter
1284129639Srwatson	NFSD_LOCK_DONTCARE();
1285129639Srwatson
128683651Speter	if (v3) {
128788091Siedowse		tl = nfsm_build_xx(NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
128888091Siedowse		*tl++ = txdr_unsigned(NFSX_V3FH);
128988091Siedowse		bcopy(f, tl, NFSX_V3FH);
129083651Speter	} else {
129188091Siedowse		tl = nfsm_build_xx(NFSX_V2FH, mb, bpos);
129288091Siedowse		bcopy(f, tl, NFSX_V2FH);
129383651Speter	}
129483651Speter}
129583651Speter
129683651Spetervoid
129788091Siedowsenfsm_srvpostop_fh_xx(fhandle_t *f, struct mbuf **mb, caddr_t *bpos)
129883651Speter{
129988091Siedowse	u_int32_t *tl;
130084002Speter
130188091Siedowse	tl = nfsm_build_xx(2 * NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
130289094Smsmith	*tl++ = nfsrv_nfs_true;
130388091Siedowse	*tl++ = txdr_unsigned(NFSX_V3FH);
130488091Siedowse	bcopy(f, tl, NFSX_V3FH);
130583651Speter}
130683651Speter
130783651Speterint
130888091Siedowsenfsm_srvstrsiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
130983651Speter{
131088091Siedowse	u_int32_t *tl;
131183651Speter
1312129639Srwatson	NFSD_LOCK_DONTCARE();
1313129639Srwatson
1314140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
131588091Siedowse	if (tl == NULL)
131684057Speter		return EBADRPC;
131788091Siedowse	*s = fxdr_unsigned(int32_t, *tl);
131883651Speter	if (*s > m || *s <= 0)
131983651Speter		return EBADRPC;
132083651Speter	return 0;
132183651Speter}
132283651Speter
132383651Speterint
1324106264Sjeffnfsm_srvnamesiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
132583651Speter{
132688091Siedowse	u_int32_t *tl;
132783651Speter
1328129639Srwatson	NFSD_LOCK_DONTCARE();
1329129639Srwatson
1330140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
133188091Siedowse	if (tl == NULL)
133284057Speter		return EBADRPC;
133388091Siedowse	*s = fxdr_unsigned(int32_t, *tl);
1334106264Sjeff	if (*s > m)
133583651Speter		return NFSERR_NAMETOL;
133683651Speter	if (*s <= 0)
133783651Speter		return EBADRPC;
133883651Speter	return 0;
133983651Speter}
134083651Speter
134183651Spetervoid
134283651Speternfsm_clget_xx(u_int32_t **tl, struct mbuf *mb, struct mbuf **mp,
1343129639Srwatson    char **bp, char **be, caddr_t bpos, int droplock)
134483651Speter{
134583651Speter	struct mbuf *nmp;
134683651Speter
1347129639Srwatson	NFSD_LOCK_DONTCARE();
1348129639Srwatson
1349129639Srwatson	if (droplock)
1350129639Srwatson		NFSD_LOCK_ASSERT();
1351129639Srwatson	else
1352129639Srwatson		NFSD_UNLOCK_ASSERT();
1353129639Srwatson
135483651Speter	if (*bp >= *be) {
135583651Speter		if (*mp == mb)
135683651Speter			(*mp)->m_len += *bp - bpos;
1357129639Srwatson		if (droplock)
1358129639Srwatson			NFSD_UNLOCK();
1359111119Simp		MGET(nmp, M_TRYWAIT, MT_DATA);
1360111119Simp		MCLGET(nmp, M_TRYWAIT);
1361129639Srwatson		if (droplock)
1362129639Srwatson			NFSD_LOCK();
136383651Speter		nmp->m_len = NFSMSIZ(nmp);
136483651Speter		(*mp)->m_next = nmp;
136583651Speter		*mp = nmp;
136683651Speter		*bp = mtod(*mp, caddr_t);
136783651Speter		*be = *bp + (*mp)->m_len;
136883651Speter	}
136983651Speter	*tl = (u_int32_t *)*bp;
137083651Speter}
137183651Speter
137283651Speterint
137388091Siedowsenfsm_srvmtofh_xx(fhandle_t *f, struct nfsrv_descript *nfsd, struct mbuf **md,
137488091Siedowse    caddr_t *dpos)
137583651Speter{
137688091Siedowse	u_int32_t *tl;
137783651Speter	int fhlen;
137883651Speter
1379129639Srwatson	NFSD_LOCK_DONTCARE();
1380129639Srwatson
138183651Speter	if (nfsd->nd_flag & ND_NFSV3) {
1382140495Sps		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
138388091Siedowse		if (tl == NULL)
138484057Speter			return EBADRPC;
138588091Siedowse		fhlen = fxdr_unsigned(int, *tl);
138683651Speter		if (fhlen != 0 && fhlen != NFSX_V3FH)
138783651Speter			return EBADRPC;
138883651Speter	} else {
138983651Speter		fhlen = NFSX_V2FH;
139083651Speter	}
139183651Speter	if (fhlen != 0) {
1392140495Sps		tl = nfsm_dissect_xx_nonblock(fhlen, md, dpos);
139388091Siedowse		if (tl == NULL)
139484057Speter			return EBADRPC;
139588091Siedowse		bcopy((caddr_t)tl, (caddr_t)(f), fhlen);
139683651Speter	} else {
139783651Speter		bzero((caddr_t)(f), NFSX_V3FH);
139883651Speter	}
139983651Speter	return 0;
140083651Speter}
140183651Speter
140283651Speterint
140388091Siedowsenfsm_srvsattr_xx(struct vattr *a, struct mbuf **md, caddr_t *dpos)
140483651Speter{
140588091Siedowse	u_int32_t *tl;
1406157391Scel	int toclient = 0;
140783651Speter
1408129639Srwatson	NFSD_LOCK_DONTCARE();
1409129639Srwatson
1410140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
141188091Siedowse	if (tl == NULL)
141284057Speter		return EBADRPC;
141389094Smsmith	if (*tl == nfsrv_nfs_true) {
1414140495Sps		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
141588091Siedowse		if (tl == NULL)
141684057Speter			return EBADRPC;
141788091Siedowse		(a)->va_mode = nfstov_mode(*tl);
141883651Speter	}
1419140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
142088091Siedowse	if (tl == NULL)
142184057Speter		return EBADRPC;
142289094Smsmith	if (*tl == nfsrv_nfs_true) {
1423140495Sps		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
142488091Siedowse		if (tl == NULL)
142584057Speter			return EBADRPC;
142688091Siedowse		(a)->va_uid = fxdr_unsigned(uid_t, *tl);
142783651Speter	}
1428140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
142988091Siedowse	if (tl == NULL)
143084057Speter		return EBADRPC;
143189094Smsmith	if (*tl == nfsrv_nfs_true) {
1432140495Sps		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
143388091Siedowse		if (tl == NULL)
143484057Speter			return EBADRPC;
143588091Siedowse		(a)->va_gid = fxdr_unsigned(gid_t, *tl);
143683651Speter	}
1437140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
143888091Siedowse	if (tl == NULL)
143984057Speter		return EBADRPC;
144089094Smsmith	if (*tl == nfsrv_nfs_true) {
1441140495Sps		tl = nfsm_dissect_xx_nonblock(2 * NFSX_UNSIGNED, md, dpos);
144288091Siedowse		if (tl == NULL)
144384057Speter			return EBADRPC;
144488091Siedowse		(a)->va_size = fxdr_hyper(tl);
144583651Speter	}
1446140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
144788091Siedowse	if (tl == NULL)
144884057Speter		return EBADRPC;
144988091Siedowse	switch (fxdr_unsigned(int, *tl)) {
145083651Speter	case NFSV3SATTRTIME_TOCLIENT:
1451140495Sps		tl = nfsm_dissect_xx_nonblock(2 * NFSX_UNSIGNED, md, dpos);
145288091Siedowse		if (tl == NULL)
145384057Speter			return EBADRPC;
145488091Siedowse		fxdr_nfsv3time(tl, &(a)->va_atime);
1455157391Scel		toclient = 1;
145683651Speter		break;
145783651Speter	case NFSV3SATTRTIME_TOSERVER:
145883651Speter		getnanotime(&(a)->va_atime);
1459157391Scel		a->va_vaflags |= VA_UTIMES_NULL;
146083651Speter		break;
146183651Speter	}
1462140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
146388091Siedowse	if (tl == NULL)
146484057Speter		return EBADRPC;
146588091Siedowse	switch (fxdr_unsigned(int, *tl)) {
146683651Speter	case NFSV3SATTRTIME_TOCLIENT:
1467140495Sps		tl = nfsm_dissect_xx_nonblock(2 * NFSX_UNSIGNED, md, dpos);
146888091Siedowse		if (tl == NULL)
146984057Speter			return EBADRPC;
147088091Siedowse		fxdr_nfsv3time(tl, &(a)->va_mtime);
1471157391Scel		a->va_vaflags &= ~VA_UTIMES_NULL;
147283651Speter		break;
147383651Speter	case NFSV3SATTRTIME_TOSERVER:
147483651Speter		getnanotime(&(a)->va_mtime);
1475157391Scel		if (toclient == 0)
1476157391Scel			a->va_vaflags |= VA_UTIMES_NULL;
147783651Speter		break;
147883651Speter	}
147983651Speter	return 0;
148083651Speter}
1481