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$");
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,
555241394Skevlo	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;
5971541Srgrimes
59899797Sdillon	*retdirp = NULL;
599105481Srwatson	cnp->cn_flags |= NOMACCHECK;
600111119Simp	cnp->cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
60129653Sdyson
6021541Srgrimes	/*
6031541Srgrimes	 * Copy the name from the mbuf list to ndp->ni_pnbuf
6041541Srgrimes	 * and set the various ndp fields appropriately.
6051541Srgrimes	 */
6061541Srgrimes	fromcp = *dposp;
6071541Srgrimes	tocp = cnp->cn_pnbuf;
6081541Srgrimes	md = *mdp;
6091541Srgrimes	rem = mtod(md, caddr_t) + md->m_len - fromcp;
6101541Srgrimes	for (i = 0; i < len; i++) {
6111541Srgrimes		while (rem == 0) {
6121541Srgrimes			md = md->m_next;
6131541Srgrimes			if (md == NULL) {
6141541Srgrimes				error = EBADRPC;
615167665Sjeff				goto out;
6161541Srgrimes			}
6171541Srgrimes			fromcp = mtod(md, caddr_t);
6181541Srgrimes			rem = md->m_len;
6191541Srgrimes		}
62027446Sdfr		if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) {
6219336Sdfr			error = EACCES;
622167665Sjeff			goto out;
6231541Srgrimes		}
6241541Srgrimes		*tocp++ = *fromcp++;
6251541Srgrimes		rem--;
6261541Srgrimes	}
6271541Srgrimes	*tocp = '\0';
6281541Srgrimes	*mdp = md;
6291541Srgrimes	*dposp = fromcp;
6301541Srgrimes	len = nfsm_rndup(len)-len;
6311541Srgrimes	if (len > 0) {
6321541Srgrimes		if (rem >= len)
6331541Srgrimes			*dposp += len;
63427609Sdfr		else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
635167665Sjeff			goto out;
6361541Srgrimes	}
63727446Sdfr
638216632Spjd	if (!pubflag && nfs_ispublicfh(fhp))
639216632Spjd		return (ESTALE);
640216632Spjd
6411541Srgrimes	/*
6421541Srgrimes	 * Extract and set starting directory.
6431541Srgrimes	 */
644241896Skib	error = nfsrv_fhtovp(fhp, 0, &dp, nfsd, slp, nam, &rdonly);
64527446Sdfr	if (error)
6461541Srgrimes		goto out;
6471541Srgrimes	if (dp->v_type != VDIR) {
648216632Spjd		vput(dp);
6491541Srgrimes		error = ENOTDIR;
6501541Srgrimes		goto out;
6511541Srgrimes	}
65227446Sdfr
65327446Sdfr	if (rdonly)
65427446Sdfr		cnp->cn_flags |= RDONLY;
65527446Sdfr
65648125Sjulian	/*
65783651Speter	 * Set return directory.  Reference to dp is implicitly transfered
65848125Sjulian	 * to the returned pointer
65948125Sjulian	 */
66027609Sdfr	*retdirp = dp;
661115301Struckman	if (v3) {
662115301Struckman		*retdirattr_retp = VOP_GETATTR(dp, retdirattrp,
663182371Sattilio			ndp->ni_cnd.cn_cred);
664115301Struckman	}
66527609Sdfr
666216632Spjd	VOP_UNLOCK(dp, 0);
667216632Spjd
66827446Sdfr	if (pubflag) {
66927446Sdfr		/*
67027446Sdfr		 * Oh joy. For WebNFS, handle those pesky '%' escapes,
67127446Sdfr		 * and the 'native path' indicator.
67227446Sdfr		 */
673111119Simp		cp = uma_zalloc(namei_zone, M_WAITOK);
67427446Sdfr		fromcp = cnp->cn_pnbuf;
67527446Sdfr		tocp = cp;
67627446Sdfr		if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) {
67727446Sdfr			switch ((unsigned char)*fromcp) {
67827446Sdfr			case WEBNFS_NATIVE_CHAR:
67927446Sdfr				/*
68027446Sdfr				 * 'Native' path for us is the same
68127446Sdfr				 * as a path according to the NFS spec,
68227446Sdfr				 * just skip the escape char.
68327446Sdfr				 */
68427446Sdfr				fromcp++;
68527446Sdfr				break;
68627446Sdfr			/*
68727446Sdfr			 * More may be added in the future, range 0x80-0xff
68827446Sdfr			 */
68927446Sdfr			default:
69027446Sdfr				error = EIO;
69192783Sjeff				uma_zfree(namei_zone, cp);
69227446Sdfr				goto out;
69327446Sdfr			}
69427446Sdfr		}
69527446Sdfr		/*
69627446Sdfr		 * Translate the '%' escapes, URL-style.
69727446Sdfr		 */
69827446Sdfr		while (*fromcp != '\0') {
69927446Sdfr			if (*fromcp == WEBNFS_ESC_CHAR) {
70027446Sdfr				if (fromcp[1] != '\0' && fromcp[2] != '\0') {
70127446Sdfr					fromcp++;
70227446Sdfr					*tocp++ = HEXSTRTOI(fromcp);
70327446Sdfr					fromcp += 2;
70427446Sdfr					continue;
70527446Sdfr				} else {
70627446Sdfr					error = ENOENT;
70792783Sjeff					uma_zfree(namei_zone, cp);
70827446Sdfr					goto out;
70927446Sdfr				}
71027446Sdfr			} else
71127446Sdfr				*tocp++ = *fromcp++;
71227446Sdfr		}
71327446Sdfr		*tocp = '\0';
71492783Sjeff		uma_zfree(namei_zone, cnp->cn_pnbuf);
71527446Sdfr		cnp->cn_pnbuf = cp;
71627446Sdfr	}
71727446Sdfr
71827446Sdfr	ndp->ni_pathlen = (tocp - cnp->cn_pnbuf) + 1;
71927446Sdfr	ndp->ni_segflg = UIO_SYSSPACE;
72027446Sdfr
72127446Sdfr	if (pubflag) {
72227446Sdfr		ndp->ni_rootdir = rootvnode;
72327446Sdfr		ndp->ni_loopcnt = 0;
724167665Sjeff
725241896Skib		if (cnp->cn_pnbuf[0] == '/')
72627446Sdfr			dp = rootvnode;
72727446Sdfr	} else {
72827609Sdfr		cnp->cn_flags |= NOCROSSMOUNT;
72927446Sdfr	}
73027446Sdfr
73148125Sjulian	/*
73248125Sjulian	 * Initialize for scan, set ni_startdir and bump ref on dp again
733123608Sjhb	 * because lookup() will dereference ni_startdir.
73448125Sjulian	 */
73548125Sjulian
736183103Sattilio	cnp->cn_thread = curthread;
7379336Sdfr	VREF(dp);
73848125Sjulian	ndp->ni_startdir = dp;
73927446Sdfr
740115301Struckman	if (!lockleaf)
741115301Struckman		cnp->cn_flags |= LOCKLEAF;
74248125Sjulian	for (;;) {
74348125Sjulian		cnp->cn_nameptr = cnp->cn_pnbuf;
74448125Sjulian		/*
74548125Sjulian		 * Call lookup() to do the real work.  If an error occurs,
74648125Sjulian		 * ndp->ni_vp and ni_dvp are left uninitialized or NULL and
74748125Sjulian		 * we do not have to dereference anything before returning.
74848125Sjulian		 * In either case ni_startdir will be dereferenced and NULLed
74948125Sjulian		 * out.
75048125Sjulian		 */
75148125Sjulian		error = lookup(ndp);
75248125Sjulian		if (error)
75348125Sjulian			break;
75448125Sjulian
75548125Sjulian		/*
75683651Speter		 * Check for encountering a symbolic link.  Trivial
75748125Sjulian		 * termination occurs if no symlink encountered.
75848125Sjulian		 * Note: zfree is safe because error is 0, so we will
75948125Sjulian		 * not zfree it again when we break.
76048125Sjulian		 */
76148125Sjulian		if ((cnp->cn_flags & ISSYMLINK) == 0) {
76248125Sjulian			if (cnp->cn_flags & (SAVENAME | SAVESTART))
76348125Sjulian				cnp->cn_flags |= HASBUF;
76448125Sjulian			else
76592783Sjeff				uma_zfree(namei_zone, cnp->cn_pnbuf);
766115301Struckman			if (ndp->ni_vp && !lockleaf)
767175294Sattilio				VOP_UNLOCK(ndp->ni_vp, 0);
76848125Sjulian			break;
76927446Sdfr		}
77048125Sjulian
77148125Sjulian		/*
77248125Sjulian		 * Validate symlink
77348125Sjulian		 */
7741541Srgrimes		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
775175294Sattilio			VOP_UNLOCK(ndp->ni_dvp, 0);
77627446Sdfr		if (!pubflag) {
77727446Sdfr			error = EINVAL;
77848125Sjulian			goto badlink2;
77927446Sdfr		}
78027446Sdfr
78127446Sdfr		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
78227446Sdfr			error = ELOOP;
78348125Sjulian			goto badlink2;
78427446Sdfr		}
78527609Sdfr		if (ndp->ni_pathlen > 1)
786111119Simp			cp = uma_zalloc(namei_zone, M_WAITOK);
7871541Srgrimes		else
78827446Sdfr			cp = cnp->cn_pnbuf;
78927446Sdfr		aiov.iov_base = cp;
79027446Sdfr		aiov.iov_len = MAXPATHLEN;
79127446Sdfr		auio.uio_iov = &aiov;
79227446Sdfr		auio.uio_iovcnt = 1;
79327446Sdfr		auio.uio_offset = 0;
79427446Sdfr		auio.uio_rw = UIO_READ;
79527446Sdfr		auio.uio_segflg = UIO_SYSSPACE;
79699797Sdillon		auio.uio_td = NULL;
79727446Sdfr		auio.uio_resid = MAXPATHLEN;
79827446Sdfr		error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
79927446Sdfr		if (error) {
80048125Sjulian		badlink1:
80127446Sdfr			if (ndp->ni_pathlen > 1)
80292783Sjeff				uma_zfree(namei_zone, cp);
80348125Sjulian		badlink2:
804155160Sjeff			vput(ndp->ni_vp);
80548125Sjulian			vrele(ndp->ni_dvp);
80627446Sdfr			break;
80727446Sdfr		}
80827446Sdfr		linklen = MAXPATHLEN - auio.uio_resid;
80927446Sdfr		if (linklen == 0) {
81027446Sdfr			error = ENOENT;
81148125Sjulian			goto badlink1;
81227446Sdfr		}
81327446Sdfr		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
81427446Sdfr			error = ENAMETOOLONG;
81548125Sjulian			goto badlink1;
81627446Sdfr		}
81748125Sjulian
81848125Sjulian		/*
81948125Sjulian		 * Adjust or replace path
82048125Sjulian		 */
82127446Sdfr		if (ndp->ni_pathlen > 1) {
82227446Sdfr			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
82392783Sjeff			uma_zfree(namei_zone, cnp->cn_pnbuf);
82427446Sdfr			cnp->cn_pnbuf = cp;
82527446Sdfr		} else
82627446Sdfr			cnp->cn_pnbuf[linklen] = '\0';
82727446Sdfr		ndp->ni_pathlen += linklen;
82848125Sjulian
82927446Sdfr		/*
83083651Speter		 * Cleanup refs for next loop and check if root directory
83183651Speter		 * should replace current directory.  Normally ni_dvp
83248125Sjulian		 * becomes the new base directory and is cleaned up when
83348125Sjulian		 * we loop.  Explicitly null pointers after invalidation
83448125Sjulian		 * to clarify operation.
83527446Sdfr		 */
83648125Sjulian		vput(ndp->ni_vp);
83748125Sjulian		ndp->ni_vp = NULL;
83848125Sjulian
83927446Sdfr		if (cnp->cn_pnbuf[0] == '/') {
84048125Sjulian			vrele(ndp->ni_dvp);
84148125Sjulian			ndp->ni_dvp = ndp->ni_rootdir;
84248125Sjulian			VREF(ndp->ni_dvp);
84327446Sdfr		}
84448125Sjulian		ndp->ni_startdir = ndp->ni_dvp;
84548125Sjulian		ndp->ni_dvp = NULL;
8461541Srgrimes	}
847115301Struckman	if (!lockleaf)
848115301Struckman		cnp->cn_flags &= ~LOCKLEAF;
84948125Sjulian
85048125Sjulian	/*
85148125Sjulian	 * nfs_namei() guarentees that fields will not contain garbage
85248125Sjulian	 * whether an error occurs or not.  This allows the caller to track
85348125Sjulian	 * cleanup state trivially.
85448125Sjulian	 */
8551541Srgrimesout:
85648125Sjulian	if (error) {
85792783Sjeff		uma_zfree(namei_zone, cnp->cn_pnbuf);
85848125Sjulian		ndp->ni_vp = NULL;
85948125Sjulian		ndp->ni_dvp = NULL;
86048125Sjulian		ndp->ni_startdir = NULL;
86148125Sjulian		cnp->cn_flags &= ~HASBUF;
86248125Sjulian	} else if ((ndp->ni_cnd.cn_flags & (WANTPARENT|LOCKPARENT)) == 0) {
86348125Sjulian		ndp->ni_dvp = NULL;
86448125Sjulian	}
8651541Srgrimes	return (error);
8661541Srgrimes}
8671541Srgrimes
8681541Srgrimes/*
8691541Srgrimes * A fiddled version of m_adj() that ensures null fill to a long
8701541Srgrimes * boundary and only trims off the back end
8711541Srgrimes */
8721541Srgrimesvoid
87383651Speternfsm_adj(struct mbuf *mp, int len, int nul)
8741541Srgrimes{
87583651Speter	struct mbuf *m;
87683651Speter	int count, i;
87783651Speter	char *cp;
8781541Srgrimes
8791541Srgrimes	/*
8801541Srgrimes	 * Trim from tail.  Scan the mbuf chain,
8811541Srgrimes	 * calculating its length and finding the last mbuf.
8821541Srgrimes	 * If the adjustment only affects this mbuf, then just
8831541Srgrimes	 * adjust and return.  Otherwise, rescan and truncate
8841541Srgrimes	 * after the remaining size.
8851541Srgrimes	 */
8861541Srgrimes	count = 0;
8871541Srgrimes	m = mp;
8881541Srgrimes	for (;;) {
8891541Srgrimes		count += m->m_len;
89099797Sdillon		if (m->m_next == NULL)
8911541Srgrimes			break;
8921541Srgrimes		m = m->m_next;
8931541Srgrimes	}
8941541Srgrimes	if (m->m_len > len) {
8951541Srgrimes		m->m_len -= len;
8961541Srgrimes		if (nul > 0) {
8971541Srgrimes			cp = mtod(m, caddr_t)+m->m_len-nul;
8981541Srgrimes			for (i = 0; i < nul; i++)
8991541Srgrimes				*cp++ = '\0';
9001541Srgrimes		}
9011541Srgrimes		return;
9021541Srgrimes	}
9031541Srgrimes	count -= len;
9041541Srgrimes	if (count < 0)
9051541Srgrimes		count = 0;
9061541Srgrimes	/*
9071541Srgrimes	 * Correct length for chain is "count".
9081541Srgrimes	 * Find the mbuf with last data, adjust its length,
9091541Srgrimes	 * and toss data from remaining mbufs on chain.
9101541Srgrimes	 */
9111541Srgrimes	for (m = mp; m; m = m->m_next) {
9121541Srgrimes		if (m->m_len >= count) {
9131541Srgrimes			m->m_len = count;
9141541Srgrimes			if (nul > 0) {
9151541Srgrimes				cp = mtod(m, caddr_t)+m->m_len-nul;
9161541Srgrimes				for (i = 0; i < nul; i++)
9171541Srgrimes					*cp++ = '\0';
9181541Srgrimes			}
919144246Ssam			if (m->m_next != NULL) {
920144246Ssam				m_freem(m->m_next);
921144246Ssam				m->m_next = NULL;
922144246Ssam			}
9231541Srgrimes			break;
9241541Srgrimes		}
9251541Srgrimes		count -= m->m_len;
9261541Srgrimes	}
9271541Srgrimes}
9281541Srgrimes
9291541Srgrimes/*
9309336Sdfr * Make these functions instead of macros, so that the kernel text size
9319336Sdfr * doesn't get too big...
9329336Sdfr */
9339336Sdfrvoid
93483651Speternfsm_srvwcc(struct nfsrv_descript *nfsd, int before_ret,
93583651Speter    struct vattr *before_vap, int after_ret, struct vattr *after_vap,
93683651Speter    struct mbuf **mbp, char **bposp)
9379336Sdfr{
93883651Speter	struct mbuf *mb = *mbp;
93983651Speter	char *bpos = *bposp;
94083651Speter	u_int32_t *tl;
9419336Sdfr
9429336Sdfr	if (before_ret) {
94384002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
94489094Smsmith		*tl = nfsrv_nfs_false;
9459336Sdfr	} else {
94684002Speter		tl = nfsm_build(u_int32_t *, 7 * NFSX_UNSIGNED);
94789094Smsmith		*tl++ = nfsrv_nfs_true;
94847751Speter		txdr_hyper(before_vap->va_size, tl);
9499336Sdfr		tl += 2;
9509336Sdfr		txdr_nfsv3time(&(before_vap->va_mtime), tl);
9519336Sdfr		tl += 2;
9529336Sdfr		txdr_nfsv3time(&(before_vap->va_ctime), tl);
9539336Sdfr	}
9549336Sdfr	*bposp = bpos;
9559336Sdfr	*mbp = mb;
9569336Sdfr	nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp);
9579336Sdfr}
9589336Sdfr
9599336Sdfrvoid
96083651Speternfsm_srvpostopattr(struct nfsrv_descript *nfsd, int after_ret,
96183651Speter    struct vattr *after_vap, struct mbuf **mbp, char **bposp)
9629336Sdfr{
96383651Speter	struct mbuf *mb = *mbp;
96483651Speter	char *bpos = *bposp;
96583651Speter	u_int32_t *tl;
96683651Speter	struct nfs_fattr *fp;
9679336Sdfr
9689336Sdfr	if (after_ret) {
96984002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
97089094Smsmith		*tl = nfsrv_nfs_false;
9719336Sdfr	} else {
97284002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED + NFSX_V3FATTR);
97389094Smsmith		*tl++ = nfsrv_nfs_true;
9749336Sdfr		fp = (struct nfs_fattr *)tl;
9759336Sdfr		nfsm_srvfattr(nfsd, after_vap, fp);
9769336Sdfr	}
9779336Sdfr	*mbp = mb;
9789336Sdfr	*bposp = bpos;
9799336Sdfr}
9809336Sdfr
9819336Sdfrvoid
98283651Speternfsm_srvfattr(struct nfsrv_descript *nfsd, struct vattr *vap,
98383651Speter    struct nfs_fattr *fp)
9849336Sdfr{
9859336Sdfr
9869336Sdfr	fp->fa_nlink = txdr_unsigned(vap->va_nlink);
9879336Sdfr	fp->fa_uid = txdr_unsigned(vap->va_uid);
9889336Sdfr	fp->fa_gid = txdr_unsigned(vap->va_gid);
9899336Sdfr	if (nfsd->nd_flag & ND_NFSV3) {
9909336Sdfr		fp->fa_type = vtonfsv3_type(vap->va_type);
9919336Sdfr		fp->fa_mode = vtonfsv3_mode(vap->va_mode);
99247751Speter		txdr_hyper(vap->va_size, &fp->fa3_size);
99347751Speter		txdr_hyper(vap->va_bytes, &fp->fa3_used);
994187830Sed		fp->fa3_rdev.specdata1 = txdr_unsigned(major(vap->va_rdev));
995187830Sed		fp->fa3_rdev.specdata2 = txdr_unsigned(minor(vap->va_rdev));
9969336Sdfr		fp->fa3_fsid.nfsuquad[0] = 0;
9979336Sdfr		fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
9989336Sdfr		fp->fa3_fileid.nfsuquad[0] = 0;
9999336Sdfr		fp->fa3_fileid.nfsuquad[1] = txdr_unsigned(vap->va_fileid);
10009336Sdfr		txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
10019336Sdfr		txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
10029336Sdfr		txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
10039336Sdfr	} else {
10049336Sdfr		fp->fa_type = vtonfsv2_type(vap->va_type);
10059336Sdfr		fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
10069336Sdfr		fp->fa2_size = txdr_unsigned(vap->va_size);
10079336Sdfr		fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
10089336Sdfr		if (vap->va_type == VFIFO)
10099336Sdfr			fp->fa2_rdev = 0xffffffff;
10109336Sdfr		else
10119336Sdfr			fp->fa2_rdev = txdr_unsigned(vap->va_rdev);
10129336Sdfr		fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
10139336Sdfr		fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
10149336Sdfr		fp->fa2_fileid = txdr_unsigned(vap->va_fileid);
10159336Sdfr		txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
10169336Sdfr		txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
10179336Sdfr		txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
10189336Sdfr	}
10199336Sdfr}
10209336Sdfr
10219336Sdfr/*
10221541Srgrimes * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
10231541Srgrimes * 	- look up fsid in mount list (if not found ret error)
10241541Srgrimes *	- get vp and export rights by calling VFS_FHTOVP()
10251541Srgrimes *	- if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
10261541Srgrimes */
10271549Srgrimesint
1028241896Skibnfsrv_fhtovp(fhandle_t *fhp, int flags, struct vnode **vpp,
1029184588Sdfr    struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
1030216632Spjd    struct sockaddr *nam, int *rdonlyp)
10311541Srgrimes{
103283651Speter	struct mount *mp;
103383651Speter	int i;
1034184588Sdfr	struct ucred *cred, *credanon;
10351541Srgrimes	int error, exflags;
103636534Speter#ifdef MNT_EXNORESPORT		/* XXX needs mountd and /etc/exports help yet */
103736534Speter	struct sockaddr_int *saddr;
103836534Speter#endif
1039184588Sdfr	int credflavor;
1040184588Sdfr	int numsecflavors, *secflavors;
1041184693Sdfr	int authsys;
1042184588Sdfr	int v3 = nfsd->nd_flag & ND_NFSV3;
1043184588Sdfr	int mountreq;
10441541Srgrimes
104599797Sdillon	*vpp = NULL;
104627446Sdfr
104727446Sdfr	if (nfs_ispublicfh(fhp)) {
1048216632Spjd		if (!nfs_pub.np_valid)
104927446Sdfr			return (ESTALE);
105027446Sdfr		fhp = &nfs_pub.np_handle;
105127446Sdfr	}
105227446Sdfr
1053185432Skib	mp = vfs_busyfs(&fhp->fh_fsid);
10543305Sphk	if (!mp)
10551541Srgrimes		return (ESTALE);
1056184588Sdfr	error = VFS_CHECKEXP(mp, nam, &exflags, &credanon,
1057184588Sdfr	    &numsecflavors, &secflavors);
1058185432Skib	if (error) {
1059185432Skib		vfs_unbusy(mp);
1060129639Srwatson		goto out;
1061185432Skib	}
1062184693Sdfr	if (numsecflavors == 0) {
1063184693Sdfr		/*
1064184693Sdfr		 * This can happen if the system is running with an
1065184693Sdfr		 * old mountd that doesn't pass in a secflavor list.
1066184693Sdfr		 */
1067184693Sdfr		numsecflavors = 1;
1068195202Sdfr		authsys = AUTH_SYS;
1069184693Sdfr		secflavors = &authsys;
1070184693Sdfr	}
1071184588Sdfr	credflavor = nfsd->nd_credflavor;
1072184588Sdfr	for (i = 0; i < numsecflavors; i++) {
1073184588Sdfr		if (secflavors[i] == credflavor)
1074184588Sdfr			break;
1075184588Sdfr	}
1076184588Sdfr	if (i == numsecflavors) {
1077184588Sdfr		/*
1078184588Sdfr		 * RFC 2623 section 2.3.2 - allow certain procedures
1079184588Sdfr		 * used at NFS client mount time even if they have
1080184588Sdfr		 * weak authentication.
1081184588Sdfr		 */
1082184588Sdfr		mountreq = FALSE;
1083184588Sdfr		if (v3) {
1084184868Sdfr			if (nfsd->nd_procnum == NFSPROC_FSINFO
1085184868Sdfr			    || nfsd->nd_procnum == NFSPROC_GETATTR)
1086184588Sdfr				mountreq = TRUE;
1087184588Sdfr		} else {
1088184588Sdfr			if (nfsd->nd_procnum == NFSPROC_FSSTAT
1089184588Sdfr			    || nfsd->nd_procnum == NFSPROC_GETATTR)
1090184588Sdfr				mountreq = TRUE;
1091184588Sdfr		}
1092184588Sdfr		if (!mountreq) {
1093184868Sdfr			error = NFSERR_AUTHERR | AUTH_TOOWEAK;
1094185432Skib			vfs_unbusy(mp);
1095184588Sdfr			goto out;
1096184588Sdfr		}
1097184588Sdfr	}
1098222167Srmacklem	error = VFS_FHTOVP(mp, &fhp->fh_fid, LK_EXCLUSIVE, vpp);
1099216632Spjd	if (error) {
1100205661Srmacklem		/* Make sure the server replies ESTALE to the client. */
1101205661Srmacklem		error = ESTALE;
1102216632Spjd		vfs_unbusy(mp);
1103129639Srwatson		goto out;
1104216632Spjd	}
110536534Speter#ifdef MNT_EXNORESPORT
110636534Speter	if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) {
110736534Speter		saddr = (struct sockaddr_in *)nam;
1108100134Salfred		if ((saddr->sin_family == AF_INET ||
1109100134Salfred		     saddr->sin_family == AF_INET6) &&
1110100134Salfred	/* same code for INET and INET6: sin*_port at same offet */
111136534Speter		    ntohs(saddr->sin_port) >= IPPORT_RESERVED) {
111236534Speter			vput(*vpp);
111354485Sdillon			*vpp = NULL;
1114129639Srwatson			error = NFSERR_AUTHERR | AUTH_TOOWEAK;
1115216631Spjd			vfs_unbusy(mp);
1116216631Spjd			goto out;
111736534Speter		}
111836534Speter	}
111936534Speter#endif
11201541Srgrimes	/*
11211541Srgrimes	 * Check/setup credentials.
11221541Srgrimes	 */
1123184588Sdfr	cred = nfsd->nd_cr;
112483651Speter	if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
11251541Srgrimes		cred->cr_uid = credanon->cr_uid;
1126194498Sbrooks		crsetgroups(cred, credanon->cr_ngroups, credanon->cr_groups);
11271541Srgrimes	}
11281541Srgrimes	if (exflags & MNT_EXRDONLY)
11291541Srgrimes		*rdonlyp = 1;
11301541Srgrimes	else
11311541Srgrimes		*rdonlyp = 0;
11327969Sdyson
1133216632Spjd	if (!(flags & NFSRV_FLAG_BUSY))
1134216632Spjd		vfs_unbusy(mp);
1135129639Srwatsonout:
1136191940Skan	if (credanon != NULL)
1137191940Skan		crfree(credanon);
1138191940Skan
1139164585Srwatson	return (error);
1140164585Srwatson}
1141164585Srwatson
1142164585Srwatson
114327446Sdfr/*
114427446Sdfr * WebNFS: check if a filehandle is a public filehandle. For v3, this
114527446Sdfr * means a length of 0, for v2 it means all zeroes. nfsm_srvmtofh has
114627446Sdfr * transformed this to all zeroes in both cases, so check for it.
114727446Sdfr */
114827446Sdfrint
114983651Speternfs_ispublicfh(fhandle_t *fhp)
115027446Sdfr{
115127446Sdfr	char *cp = (char *)fhp;
115227446Sdfr	int i;
115327446Sdfr
1154129639Srwatson	NFSD_LOCK_DONTCARE();
1155129639Srwatson
115627446Sdfr	for (i = 0; i < NFSX_V3FH; i++)
115727446Sdfr		if (*cp++ != 0)
115827446Sdfr			return (FALSE);
115927446Sdfr	return (TRUE);
116027446Sdfr}
116183651Speter
11621541Srgrimes/*
11639336Sdfr * Map errnos to NFS error numbers. For Version 3 also filter out error
11649336Sdfr * numbers not specified for the associated procedure.
11659336Sdfr */
11665455Sdgint
116783651Speternfsrv_errmap(struct nfsrv_descript *nd, int err)
11689336Sdfr{
1169129639Srwatson	const short *defaulterrp, *errp;
1170102236Sphk	int e;
11719336Sdfr
1172129639Srwatson
11739336Sdfr	if (nd->nd_flag & ND_NFSV3) {
11749336Sdfr	    if (nd->nd_procnum <= NFSPROC_COMMIT) {
11759336Sdfr		errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
11769336Sdfr		while (*++errp) {
11779336Sdfr			if (*errp == err)
11789336Sdfr				return (err);
11799336Sdfr			else if (*errp > err)
11809336Sdfr				break;
11819336Sdfr		}
11829336Sdfr		return ((int)*defaulterrp);
11839336Sdfr	    } else
11849336Sdfr		return (err & 0xffff);
11859336Sdfr	}
1186102236Sphk	e = 0;
11879336Sdfr	if (err <= ELAST)
1188102236Sphk		e = nfsrv_v2errmap[err - 1];
1189102236Sphk	if (e != 0)
1190102236Sphk		return (e);
11919336Sdfr	return (NFSERR_IO);
11929336Sdfr}
11939336Sdfr
119436503Speter/*
119536503Speter * Sort the group list in increasing numerical order.
119636503Speter * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
119736503Speter *  that used to be here.)
119836503Speter */
119936503Spetervoid
120083651Speternfsrvw_sort(gid_t *list, int num)
120136503Speter{
120283651Speter	int i, j;
120336503Speter	gid_t v;
120436503Speter
120536503Speter	/* Insertion sort. */
120636503Speter	for (i = 1; i < num; i++) {
120736503Speter		v = list[i];
120836503Speter		/* find correct slot for value v, moving others up */
120936503Speter		for (j = i; --j >= 0 && v < list[j];)
121036503Speter			list[j + 1] = list[j];
121136503Speter		list[j + 1] = v;
121236503Speter	}
121336503Speter}
121436503Speter
121536503Speter/*
121683651Speter * Helper functions for macros.
121783651Speter */
121883651Speter
121983651Spetervoid
122088091Siedowsenfsm_srvfhtom_xx(fhandle_t *f, int v3, struct mbuf **mb, caddr_t *bpos)
122183651Speter{
122288091Siedowse	u_int32_t *tl;
122383651Speter
122483651Speter	if (v3) {
122588091Siedowse		tl = nfsm_build_xx(NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
122688091Siedowse		*tl++ = txdr_unsigned(NFSX_V3FH);
122788091Siedowse		bcopy(f, tl, NFSX_V3FH);
122883651Speter	} else {
122988091Siedowse		tl = nfsm_build_xx(NFSX_V2FH, mb, bpos);
123088091Siedowse		bcopy(f, tl, NFSX_V2FH);
123183651Speter	}
123283651Speter}
123383651Speter
123483651Spetervoid
123588091Siedowsenfsm_srvpostop_fh_xx(fhandle_t *f, struct mbuf **mb, caddr_t *bpos)
123683651Speter{
123788091Siedowse	u_int32_t *tl;
123884002Speter
123988091Siedowse	tl = nfsm_build_xx(2 * NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
124089094Smsmith	*tl++ = nfsrv_nfs_true;
124188091Siedowse	*tl++ = txdr_unsigned(NFSX_V3FH);
124288091Siedowse	bcopy(f, tl, NFSX_V3FH);
124383651Speter}
124483651Speter
124583651Speterint
124688091Siedowsenfsm_srvstrsiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
124783651Speter{
124888091Siedowse	u_int32_t *tl;
124983651Speter
1250140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
125188091Siedowse	if (tl == NULL)
125284057Speter		return EBADRPC;
125388091Siedowse	*s = fxdr_unsigned(int32_t, *tl);
125483651Speter	if (*s > m || *s <= 0)
125583651Speter		return EBADRPC;
125683651Speter	return 0;
125783651Speter}
125883651Speter
125983651Speterint
1260106264Sjeffnfsm_srvnamesiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
126183651Speter{
126288091Siedowse	u_int32_t *tl;
126383651Speter
1264129639Srwatson	NFSD_LOCK_DONTCARE();
1265129639Srwatson
1266140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
126788091Siedowse	if (tl == NULL)
126884057Speter		return EBADRPC;
126988091Siedowse	*s = fxdr_unsigned(int32_t, *tl);
1270106264Sjeff	if (*s > m)
127183651Speter		return NFSERR_NAMETOL;
127283651Speter	if (*s <= 0)
127383651Speter		return EBADRPC;
127483651Speter	return 0;
127583651Speter}
127683651Speter
1277165739Shrsint
1278165739Shrsnfsm_srvnamesiz0_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
1279165739Shrs{
1280165739Shrs	u_int32_t *tl;
1281165739Shrs
1282165739Shrs	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
1283165739Shrs	if (tl == NULL)
1284165739Shrs		return EBADRPC;
1285165739Shrs	*s = fxdr_unsigned(int32_t, *tl);
1286165739Shrs	if (*s > m)
1287165739Shrs		return NFSERR_NAMETOL;
1288165739Shrs	if (*s < 0)
1289165739Shrs		return EBADRPC;
1290165739Shrs	return 0;
1291165739Shrs}
1292165739Shrs
129383651Spetervoid
129483651Speternfsm_clget_xx(u_int32_t **tl, struct mbuf *mb, struct mbuf **mp,
1295167665Sjeff    char **bp, char **be, caddr_t bpos)
129683651Speter{
129783651Speter	struct mbuf *nmp;
129883651Speter
1299167665Sjeff	NFSD_UNLOCK_ASSERT();
1300129639Srwatson
130183651Speter	if (*bp >= *be) {
130283651Speter		if (*mp == mb)
130383651Speter			(*mp)->m_len += *bp - bpos;
1304243882Sglebius		MGET(nmp, M_WAITOK, MT_DATA);
1305243882Sglebius		MCLGET(nmp, M_WAITOK);
130683651Speter		nmp->m_len = NFSMSIZ(nmp);
130783651Speter		(*mp)->m_next = nmp;
130883651Speter		*mp = nmp;
130983651Speter		*bp = mtod(*mp, caddr_t);
131083651Speter		*be = *bp + (*mp)->m_len;
131183651Speter	}
131283651Speter	*tl = (u_int32_t *)*bp;
131383651Speter}
131483651Speter
131583651Speterint
1316184588Sdfrnfsm_srvmtofh_xx(fhandle_t *f, int v3, struct mbuf **md, caddr_t *dpos)
131783651Speter{
131888091Siedowse	u_int32_t *tl;
131983651Speter	int fhlen;
132083651Speter
1321184588Sdfr	if (v3) {
1322140495Sps		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
132388091Siedowse		if (tl == NULL)
132484057Speter			return EBADRPC;
132588091Siedowse		fhlen = fxdr_unsigned(int, *tl);
132683651Speter		if (fhlen != 0 && fhlen != NFSX_V3FH)
132783651Speter			return EBADRPC;
132883651Speter	} else {
132983651Speter		fhlen = NFSX_V2FH;
133083651Speter	}
133183651Speter	if (fhlen != 0) {
1332140495Sps		tl = nfsm_dissect_xx_nonblock(fhlen, md, dpos);
133388091Siedowse		if (tl == NULL)
133484057Speter			return EBADRPC;
133588091Siedowse		bcopy((caddr_t)tl, (caddr_t)(f), fhlen);
133683651Speter	} else {
133783651Speter		bzero((caddr_t)(f), NFSX_V3FH);
133883651Speter	}
133983651Speter	return 0;
134083651Speter}
134183651Speter
134283651Speterint
134388091Siedowsenfsm_srvsattr_xx(struct vattr *a, struct mbuf **md, caddr_t *dpos)
134483651Speter{
134588091Siedowse	u_int32_t *tl;
1346157391Scel	int toclient = 0;
134783651Speter
1348140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
134988091Siedowse	if (tl == NULL)
135084057Speter		return EBADRPC;
135189094Smsmith	if (*tl == nfsrv_nfs_true) {
1352140495Sps		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
135388091Siedowse		if (tl == NULL)
135484057Speter			return EBADRPC;
135588091Siedowse		(a)->va_mode = nfstov_mode(*tl);
135683651Speter	}
1357140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
135888091Siedowse	if (tl == NULL)
135984057Speter		return EBADRPC;
136089094Smsmith	if (*tl == nfsrv_nfs_true) {
1361140495Sps		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
136288091Siedowse		if (tl == NULL)
136384057Speter			return EBADRPC;
136488091Siedowse		(a)->va_uid = fxdr_unsigned(uid_t, *tl);
136583651Speter	}
1366140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
136788091Siedowse	if (tl == NULL)
136884057Speter		return EBADRPC;
136989094Smsmith	if (*tl == nfsrv_nfs_true) {
1370140495Sps		tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
137188091Siedowse		if (tl == NULL)
137284057Speter			return EBADRPC;
137388091Siedowse		(a)->va_gid = fxdr_unsigned(gid_t, *tl);
137483651Speter	}
1375140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
137688091Siedowse	if (tl == NULL)
137784057Speter		return EBADRPC;
137889094Smsmith	if (*tl == nfsrv_nfs_true) {
1379140495Sps		tl = nfsm_dissect_xx_nonblock(2 * NFSX_UNSIGNED, md, dpos);
138088091Siedowse		if (tl == NULL)
138184057Speter			return EBADRPC;
138288091Siedowse		(a)->va_size = fxdr_hyper(tl);
138383651Speter	}
1384140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
138588091Siedowse	if (tl == NULL)
138684057Speter		return EBADRPC;
138788091Siedowse	switch (fxdr_unsigned(int, *tl)) {
138883651Speter	case NFSV3SATTRTIME_TOCLIENT:
1389140495Sps		tl = nfsm_dissect_xx_nonblock(2 * NFSX_UNSIGNED, md, dpos);
139088091Siedowse		if (tl == NULL)
139184057Speter			return EBADRPC;
139288091Siedowse		fxdr_nfsv3time(tl, &(a)->va_atime);
1393157391Scel		toclient = 1;
139483651Speter		break;
139583651Speter	case NFSV3SATTRTIME_TOSERVER:
1396245611Sjhb		vfs_timestamp(&a->va_atime);
1397157391Scel		a->va_vaflags |= VA_UTIMES_NULL;
139883651Speter		break;
139983651Speter	}
1400140495Sps	tl = nfsm_dissect_xx_nonblock(NFSX_UNSIGNED, md, dpos);
140188091Siedowse	if (tl == NULL)
140284057Speter		return EBADRPC;
140388091Siedowse	switch (fxdr_unsigned(int, *tl)) {
140483651Speter	case NFSV3SATTRTIME_TOCLIENT:
1405140495Sps		tl = nfsm_dissect_xx_nonblock(2 * NFSX_UNSIGNED, md, dpos);
140688091Siedowse		if (tl == NULL)
140784057Speter			return EBADRPC;
140888091Siedowse		fxdr_nfsv3time(tl, &(a)->va_mtime);
1409157391Scel		a->va_vaflags &= ~VA_UTIMES_NULL;
141083651Speter		break;
141183651Speter	case NFSV3SATTRTIME_TOSERVER:
1412245611Sjhb		vfs_timestamp(&a->va_mtime);
1413157391Scel		if (toclient == 0)
1414157391Scel			a->va_vaflags |= VA_UTIMES_NULL;
141583651Speter		break;
141683651Speter	}
141783651Speter	return 0;
141883651Speter}
1419