nfs_srvsubs.c revision 89094
11541Srgrimes/*
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 * 3. All advertising materials mentioning features or use of this software
171541Srgrimes *    must display the following acknowledgement:
181541Srgrimes *	This product includes software developed by the University of
191541Srgrimes *	California, Berkeley and its contributors.
201541Srgrimes * 4. Neither the name of the University nor the names of its contributors
211541Srgrimes *    may be used to endorse or promote products derived from this software
221541Srgrimes *    without specific prior written permission.
231541Srgrimes *
241541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341541Srgrimes * SUCH DAMAGE.
351541Srgrimes *
3636503Speter *	@(#)nfs_subs.c  8.8 (Berkeley) 5/22/95
3750477Speter * $FreeBSD: head/sys/nfsserver/nfs_srvsubs.c 89094 2002-01-08 19:41:06Z msmith $
381541Srgrimes */
391541Srgrimes
4083651Speter#include <sys/cdefs.h>
4183651Speter__FBSDID("$FreeBSD: head/sys/nfsserver/nfs_srvsubs.c 89094 2002-01-08 19:41:06Z msmith $");
4283651Speter
431541Srgrimes/*
441541Srgrimes * These functions support the macros and help fiddle mbuf chains for
451541Srgrimes * the nfs op functions. They do things like create the rpc header and
461541Srgrimes * copy data between mbuf chains and uio lists.
471541Srgrimes */
4883651Speter
491541Srgrimes#include <sys/param.h>
5048274Speter#include <sys/systm.h>
5148274Speter#include <sys/kernel.h>
5260041Sphk#include <sys/bio.h>
5331886Sbde#include <sys/buf.h>
541541Srgrimes#include <sys/proc.h>
551541Srgrimes#include <sys/mount.h>
561541Srgrimes#include <sys/vnode.h>
571541Srgrimes#include <sys/namei.h>
581541Srgrimes#include <sys/mbuf.h>
591541Srgrimes#include <sys/socket.h>
601541Srgrimes#include <sys/stat.h>
619336Sdfr#include <sys/malloc.h>
6283700Speter#include <sys/module.h>
632997Swollman#include <sys/sysent.h>
642997Swollman#include <sys/syscall.h>
6583651Speter#include <sys/sysproto.h>
661541Srgrimes
673305Sphk#include <vm/vm.h>
6812662Sdg#include <vm/vm_object.h>
6912662Sdg#include <vm/vm_extern.h>
7032011Sbde#include <vm/vm_zone.h>
713305Sphk
721541Srgrimes#include <nfs/rpcv2.h>
739336Sdfr#include <nfs/nfsproto.h>
7483651Speter#include <nfsserver/nfs.h>
751541Srgrimes#include <nfs/xdr_subs.h>
7683651Speter#include <nfsserver/nfsm_subs.h>
771541Srgrimes
781541Srgrimes#include <netinet/in.h>
791541Srgrimes
801541Srgrimes/*
811541Srgrimes * Data items converted to xdr at startup, since they are constant
821541Srgrimes * This is kinda hokey, but may save a little time doing byte swaps
831541Srgrimes */
8489094Smsmithu_int32_t nfsrv_nfs_xdrneg1;
8589094Smsmithu_int32_t nfsrv_rpc_call, nfsrv_rpc_vers, nfsrv_rpc_reply,
8689094Smsmith	nfsrv_rpc_msgdenied, nfsrv_rpc_autherr,
8789094Smsmith	nfsrv_rpc_mismatch, nfsrv_rpc_auth_unix, nfsrv_rpc_msgaccepted;
8889094Smsmithu_int32_t nfsrv_nfs_prog, nfsrv_nfs_true, nfsrv_nfs_false;
891541Srgrimes
901541Srgrimes/* And other global data */
9183651Speterstatic nfstype nfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK,
9283651Speter				 NFNON, NFCHR, NFNON };
9383651Speter#define vtonfsv2_type(a)	txdr_unsigned(nfsv2_type[((int32_t)(a))])
9483651Speter#define vtonfsv3_mode(m)	txdr_unsigned((m) & ALLPERMS)
9512911Sphk
9689094Smsmithint nfsrv_ticks;
979336Sdfr
989759Sbdestruct nfssvc_sockhead nfssvc_sockhead;
999759Sbdeint nfssvc_sockhead_flag;
1009759Sbdestruct nfsd_head nfsd_head;
1019759Sbdeint nfsd_head_flag;
1029759Sbde
10338894Sbdestatic int nfs_prev_nfssvc_sy_narg;
10438894Sbdestatic sy_call_t *nfs_prev_nfssvc_sy_call;
10538894Sbde
1069336Sdfr/*
1079336Sdfr * Mapping of old NFS Version 2 RPC numbers to generic numbers.
1089336Sdfr */
10989094Smsmithint 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 */
13883651Speterint 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.
1669336Sdfr * Use NFSERR_IO as the catch all for ones not specifically defined in
1679336Sdfr * RFC 1094.
1689336Sdfr */
1699336Sdfrstatic u_char nfsrv_v2errmap[ELAST] = {
1709336Sdfr  NFSERR_PERM,	NFSERR_NOENT,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1719336Sdfr  NFSERR_NXIO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1729336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_ACCES,	NFSERR_IO,	NFSERR_IO,
1739336Sdfr  NFSERR_IO,	NFSERR_EXIST,	NFSERR_IO,	NFSERR_NODEV,	NFSERR_NOTDIR,
1749336Sdfr  NFSERR_ISDIR,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1759336Sdfr  NFSERR_IO,	NFSERR_FBIG,	NFSERR_NOSPC,	NFSERR_IO,	NFSERR_ROFS,
1769336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1779336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1789336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1799336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1809336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1819336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1829336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_NAMETOL,	NFSERR_IO,	NFSERR_IO,
1839336Sdfr  NFSERR_NOTEMPTY, NFSERR_IO,	NFSERR_IO,	NFSERR_DQUOT,	NFSERR_STALE,
1849336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1859336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
18641796Sdt  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
18741796Sdt  NFSERR_IO /* << Last is 86 */
1889336Sdfr};
1899336Sdfr
1909336Sdfr/*
1919336Sdfr * Maps errno values to nfs error numbers.
1929336Sdfr * Although it is not obvious whether or not NFS clients really care if
1939336Sdfr * a returned error value is in the specified list for the procedure, the
1949336Sdfr * safest thing to do is filter them appropriately. For Version 2, the
1959336Sdfr * X/Open XNFS document is the only specification that defines error values
1969336Sdfr * for each RPC (The RFC simply lists all possible error values for all RPCs),
1979336Sdfr * so I have decided to not do this for Version 2.
1989336Sdfr * The first entry is the default error return and the rest are the valid
1999336Sdfr * errors for that RPC in increasing numeric order.
2009336Sdfr */
2019336Sdfrstatic short nfsv3err_null[] = {
2029336Sdfr	0,
2039336Sdfr	0,
2049336Sdfr};
2059336Sdfr
2069336Sdfrstatic short nfsv3err_getattr[] = {
2079336Sdfr	NFSERR_IO,
2089336Sdfr	NFSERR_IO,
2099336Sdfr	NFSERR_STALE,
2109336Sdfr	NFSERR_BADHANDLE,
2119336Sdfr	NFSERR_SERVERFAULT,
2129336Sdfr	0,
2139336Sdfr};
2149336Sdfr
2159336Sdfrstatic short nfsv3err_setattr[] = {
2169336Sdfr	NFSERR_IO,
2179336Sdfr	NFSERR_PERM,
2189336Sdfr	NFSERR_IO,
2199336Sdfr	NFSERR_ACCES,
2209336Sdfr	NFSERR_INVAL,
2219336Sdfr	NFSERR_NOSPC,
2229336Sdfr	NFSERR_ROFS,
2239336Sdfr	NFSERR_DQUOT,
2249336Sdfr	NFSERR_STALE,
2259336Sdfr	NFSERR_BADHANDLE,
2269336Sdfr	NFSERR_NOT_SYNC,
2279336Sdfr	NFSERR_SERVERFAULT,
2289336Sdfr	0,
2299336Sdfr};
2309336Sdfr
2319336Sdfrstatic short nfsv3err_lookup[] = {
2329336Sdfr	NFSERR_IO,
2339336Sdfr	NFSERR_NOENT,
2349336Sdfr	NFSERR_IO,
2359336Sdfr	NFSERR_ACCES,
2369336Sdfr	NFSERR_NOTDIR,
2379336Sdfr	NFSERR_NAMETOL,
2389336Sdfr	NFSERR_STALE,
2399336Sdfr	NFSERR_BADHANDLE,
2409336Sdfr	NFSERR_SERVERFAULT,
2419336Sdfr	0,
2429336Sdfr};
2439336Sdfr
2449336Sdfrstatic short nfsv3err_access[] = {
2459336Sdfr	NFSERR_IO,
2469336Sdfr	NFSERR_IO,
2479336Sdfr	NFSERR_STALE,
2489336Sdfr	NFSERR_BADHANDLE,
2499336Sdfr	NFSERR_SERVERFAULT,
2509336Sdfr	0,
2519336Sdfr};
2529336Sdfr
2539336Sdfrstatic short nfsv3err_readlink[] = {
2549336Sdfr	NFSERR_IO,
2559336Sdfr	NFSERR_IO,
2569336Sdfr	NFSERR_ACCES,
2579336Sdfr	NFSERR_INVAL,
2589336Sdfr	NFSERR_STALE,
2599336Sdfr	NFSERR_BADHANDLE,
2609336Sdfr	NFSERR_NOTSUPP,
2619336Sdfr	NFSERR_SERVERFAULT,
2629336Sdfr	0,
2639336Sdfr};
2649336Sdfr
2659336Sdfrstatic short nfsv3err_read[] = {
2669336Sdfr	NFSERR_IO,
2679336Sdfr	NFSERR_IO,
2689336Sdfr	NFSERR_NXIO,
2699336Sdfr	NFSERR_ACCES,
2709336Sdfr	NFSERR_INVAL,
2719336Sdfr	NFSERR_STALE,
2729336Sdfr	NFSERR_BADHANDLE,
2739336Sdfr	NFSERR_SERVERFAULT,
2749336Sdfr	0,
2759336Sdfr};
2769336Sdfr
2779336Sdfrstatic short nfsv3err_write[] = {
2789336Sdfr	NFSERR_IO,
2799336Sdfr	NFSERR_IO,
2809336Sdfr	NFSERR_ACCES,
2819336Sdfr	NFSERR_INVAL,
2829336Sdfr	NFSERR_FBIG,
2839336Sdfr	NFSERR_NOSPC,
2849336Sdfr	NFSERR_ROFS,
2859336Sdfr	NFSERR_DQUOT,
2869336Sdfr	NFSERR_STALE,
2879336Sdfr	NFSERR_BADHANDLE,
2889336Sdfr	NFSERR_SERVERFAULT,
2899336Sdfr	0,
2909336Sdfr};
2919336Sdfr
2929336Sdfrstatic short nfsv3err_create[] = {
2939336Sdfr	NFSERR_IO,
2949336Sdfr	NFSERR_IO,
2959336Sdfr	NFSERR_ACCES,
2969336Sdfr	NFSERR_EXIST,
2979336Sdfr	NFSERR_NOTDIR,
2989336Sdfr	NFSERR_NOSPC,
2999336Sdfr	NFSERR_ROFS,
3009336Sdfr	NFSERR_NAMETOL,
3019336Sdfr	NFSERR_DQUOT,
3029336Sdfr	NFSERR_STALE,
3039336Sdfr	NFSERR_BADHANDLE,
3049336Sdfr	NFSERR_NOTSUPP,
3059336Sdfr	NFSERR_SERVERFAULT,
3069336Sdfr	0,
3079336Sdfr};
3089336Sdfr
3099336Sdfrstatic short nfsv3err_mkdir[] = {
3109336Sdfr	NFSERR_IO,
3119336Sdfr	NFSERR_IO,
3129336Sdfr	NFSERR_ACCES,
3139336Sdfr	NFSERR_EXIST,
3149336Sdfr	NFSERR_NOTDIR,
3159336Sdfr	NFSERR_NOSPC,
3169336Sdfr	NFSERR_ROFS,
3179336Sdfr	NFSERR_NAMETOL,
3189336Sdfr	NFSERR_DQUOT,
3199336Sdfr	NFSERR_STALE,
3209336Sdfr	NFSERR_BADHANDLE,
3219336Sdfr	NFSERR_NOTSUPP,
3229336Sdfr	NFSERR_SERVERFAULT,
3239336Sdfr	0,
3249336Sdfr};
3259336Sdfr
3269336Sdfrstatic short nfsv3err_symlink[] = {
3279336Sdfr	NFSERR_IO,
3289336Sdfr	NFSERR_IO,
3299336Sdfr	NFSERR_ACCES,
3309336Sdfr	NFSERR_EXIST,
3319336Sdfr	NFSERR_NOTDIR,
3329336Sdfr	NFSERR_NOSPC,
3339336Sdfr	NFSERR_ROFS,
3349336Sdfr	NFSERR_NAMETOL,
3359336Sdfr	NFSERR_DQUOT,
3369336Sdfr	NFSERR_STALE,
3379336Sdfr	NFSERR_BADHANDLE,
3389336Sdfr	NFSERR_NOTSUPP,
3399336Sdfr	NFSERR_SERVERFAULT,
3409336Sdfr	0,
3419336Sdfr};
3429336Sdfr
3439336Sdfrstatic short nfsv3err_mknod[] = {
3449336Sdfr	NFSERR_IO,
3459336Sdfr	NFSERR_IO,
3469336Sdfr	NFSERR_ACCES,
3479336Sdfr	NFSERR_EXIST,
3489336Sdfr	NFSERR_NOTDIR,
3499336Sdfr	NFSERR_NOSPC,
3509336Sdfr	NFSERR_ROFS,
3519336Sdfr	NFSERR_NAMETOL,
3529336Sdfr	NFSERR_DQUOT,
3539336Sdfr	NFSERR_STALE,
3549336Sdfr	NFSERR_BADHANDLE,
3559336Sdfr	NFSERR_NOTSUPP,
3569336Sdfr	NFSERR_SERVERFAULT,
3579336Sdfr	NFSERR_BADTYPE,
3589336Sdfr	0,
3599336Sdfr};
3609336Sdfr
3619336Sdfrstatic short nfsv3err_remove[] = {
3629336Sdfr	NFSERR_IO,
3639336Sdfr	NFSERR_NOENT,
3649336Sdfr	NFSERR_IO,
3659336Sdfr	NFSERR_ACCES,
3669336Sdfr	NFSERR_NOTDIR,
3679336Sdfr	NFSERR_ROFS,
3689336Sdfr	NFSERR_NAMETOL,
3699336Sdfr	NFSERR_STALE,
3709336Sdfr	NFSERR_BADHANDLE,
3719336Sdfr	NFSERR_SERVERFAULT,
3729336Sdfr	0,
3739336Sdfr};
3749336Sdfr
3759336Sdfrstatic short nfsv3err_rmdir[] = {
3769336Sdfr	NFSERR_IO,
3779336Sdfr	NFSERR_NOENT,
3789336Sdfr	NFSERR_IO,
3799336Sdfr	NFSERR_ACCES,
3809336Sdfr	NFSERR_EXIST,
3819336Sdfr	NFSERR_NOTDIR,
3829336Sdfr	NFSERR_INVAL,
3839336Sdfr	NFSERR_ROFS,
3849336Sdfr	NFSERR_NAMETOL,
3859336Sdfr	NFSERR_NOTEMPTY,
3869336Sdfr	NFSERR_STALE,
3879336Sdfr	NFSERR_BADHANDLE,
3889336Sdfr	NFSERR_NOTSUPP,
3899336Sdfr	NFSERR_SERVERFAULT,
3909336Sdfr	0,
3919336Sdfr};
3929336Sdfr
3939336Sdfrstatic short nfsv3err_rename[] = {
3949336Sdfr	NFSERR_IO,
3959336Sdfr	NFSERR_NOENT,
3969336Sdfr	NFSERR_IO,
3979336Sdfr	NFSERR_ACCES,
3989336Sdfr	NFSERR_EXIST,
3999336Sdfr	NFSERR_XDEV,
4009336Sdfr	NFSERR_NOTDIR,
4019336Sdfr	NFSERR_ISDIR,
4029336Sdfr	NFSERR_INVAL,
4039336Sdfr	NFSERR_NOSPC,
4049336Sdfr	NFSERR_ROFS,
4059336Sdfr	NFSERR_MLINK,
4069336Sdfr	NFSERR_NAMETOL,
4079336Sdfr	NFSERR_NOTEMPTY,
4089336Sdfr	NFSERR_DQUOT,
4099336Sdfr	NFSERR_STALE,
4109336Sdfr	NFSERR_BADHANDLE,
4119336Sdfr	NFSERR_NOTSUPP,
4129336Sdfr	NFSERR_SERVERFAULT,
4139336Sdfr	0,
4149336Sdfr};
4159336Sdfr
4169336Sdfrstatic short nfsv3err_link[] = {
4179336Sdfr	NFSERR_IO,
4189336Sdfr	NFSERR_IO,
4199336Sdfr	NFSERR_ACCES,
4209336Sdfr	NFSERR_EXIST,
4219336Sdfr	NFSERR_XDEV,
4229336Sdfr	NFSERR_NOTDIR,
4239336Sdfr	NFSERR_INVAL,
4249336Sdfr	NFSERR_NOSPC,
4259336Sdfr	NFSERR_ROFS,
4269336Sdfr	NFSERR_MLINK,
4279336Sdfr	NFSERR_NAMETOL,
4289336Sdfr	NFSERR_DQUOT,
4299336Sdfr	NFSERR_STALE,
4309336Sdfr	NFSERR_BADHANDLE,
4319336Sdfr	NFSERR_NOTSUPP,
4329336Sdfr	NFSERR_SERVERFAULT,
4339336Sdfr	0,
4349336Sdfr};
4359336Sdfr
4369336Sdfrstatic short nfsv3err_readdir[] = {
4379336Sdfr	NFSERR_IO,
4389336Sdfr	NFSERR_IO,
4399336Sdfr	NFSERR_ACCES,
4409336Sdfr	NFSERR_NOTDIR,
4419336Sdfr	NFSERR_STALE,
4429336Sdfr	NFSERR_BADHANDLE,
4439336Sdfr	NFSERR_BAD_COOKIE,
4449336Sdfr	NFSERR_TOOSMALL,
4459336Sdfr	NFSERR_SERVERFAULT,
4469336Sdfr	0,
4479336Sdfr};
4489336Sdfr
4499336Sdfrstatic short nfsv3err_readdirplus[] = {
4509336Sdfr	NFSERR_IO,
4519336Sdfr	NFSERR_IO,
4529336Sdfr	NFSERR_ACCES,
4539336Sdfr	NFSERR_NOTDIR,
4549336Sdfr	NFSERR_STALE,
4559336Sdfr	NFSERR_BADHANDLE,
4569336Sdfr	NFSERR_BAD_COOKIE,
4579336Sdfr	NFSERR_NOTSUPP,
4589336Sdfr	NFSERR_TOOSMALL,
4599336Sdfr	NFSERR_SERVERFAULT,
4609336Sdfr	0,
4619336Sdfr};
4629336Sdfr
4639336Sdfrstatic short nfsv3err_fsstat[] = {
4649336Sdfr	NFSERR_IO,
4659336Sdfr	NFSERR_IO,
4669336Sdfr	NFSERR_STALE,
4679336Sdfr	NFSERR_BADHANDLE,
4689336Sdfr	NFSERR_SERVERFAULT,
4699336Sdfr	0,
4709336Sdfr};
4719336Sdfr
4729336Sdfrstatic short nfsv3err_fsinfo[] = {
4739336Sdfr	NFSERR_STALE,
4749336Sdfr	NFSERR_STALE,
4759336Sdfr	NFSERR_BADHANDLE,
4769336Sdfr	NFSERR_SERVERFAULT,
4779336Sdfr	0,
4789336Sdfr};
4799336Sdfr
4809336Sdfrstatic short nfsv3err_pathconf[] = {
4819336Sdfr	NFSERR_STALE,
4829336Sdfr	NFSERR_STALE,
4839336Sdfr	NFSERR_BADHANDLE,
4849336Sdfr	NFSERR_SERVERFAULT,
4859336Sdfr	0,
4869336Sdfr};
4879336Sdfr
4889336Sdfrstatic short nfsv3err_commit[] = {
4899336Sdfr	NFSERR_IO,
4909336Sdfr	NFSERR_IO,
4919336Sdfr	NFSERR_STALE,
4929336Sdfr	NFSERR_BADHANDLE,
4939336Sdfr	NFSERR_SERVERFAULT,
4949336Sdfr	0,
4959336Sdfr};
4969336Sdfr
4979336Sdfrstatic short *nfsrv_v3errmap[] = {
4989336Sdfr	nfsv3err_null,
4999336Sdfr	nfsv3err_getattr,
5009336Sdfr	nfsv3err_setattr,
5019336Sdfr	nfsv3err_lookup,
5029336Sdfr	nfsv3err_access,
5039336Sdfr	nfsv3err_readlink,
5049336Sdfr	nfsv3err_read,
5059336Sdfr	nfsv3err_write,
5069336Sdfr	nfsv3err_create,
5079336Sdfr	nfsv3err_mkdir,
5089336Sdfr	nfsv3err_symlink,
5099336Sdfr	nfsv3err_mknod,
5109336Sdfr	nfsv3err_remove,
5119336Sdfr	nfsv3err_rmdir,
5129336Sdfr	nfsv3err_rename,
5139336Sdfr	nfsv3err_link,
5149336Sdfr	nfsv3err_readdir,
5159336Sdfr	nfsv3err_readdirplus,
5169336Sdfr	nfsv3err_fsstat,
5179336Sdfr	nfsv3err_fsinfo,
5189336Sdfr	nfsv3err_pathconf,
5199336Sdfr	nfsv3err_commit,
5209336Sdfr};
5219336Sdfr
5221541Srgrimes/*
5231541Srgrimes * Called once to initialize data structures...
5241541Srgrimes */
52583651Speterstatic int
52683700Speternfsrv_modevent(module_t mod, int type, void *data)
5271541Srgrimes{
5281541Srgrimes
52983700Speter	switch (type) {
53083700Speter	case MOD_LOAD:
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
54783700Speter		nfsrv_init(0);		/* Init server data structures */
54883700Speter		nfsrv_initcache();	/* Init the server request cache */
5491541Srgrimes
55083700Speter		nfsrv_timer(0);
5511541Srgrimes
55283700Speter		nfs_prev_nfssvc_sy_narg = sysent[SYS_nfssvc].sy_narg;
55383700Speter		sysent[SYS_nfssvc].sy_narg = 2;
55483700Speter		nfs_prev_nfssvc_sy_call = sysent[SYS_nfssvc].sy_call;
55583700Speter		sysent[SYS_nfssvc].sy_call = (sy_call_t *)nfssvc;
55683700Speter		break;
5572997Swollman
55883700Speter		case MOD_UNLOAD:
55983700Speter
56083700Speter		untimeout(nfsrv_timer, (void *)NULL, nfsrv_timer_handle);
56183700Speter		sysent[SYS_nfssvc].sy_narg = nfs_prev_nfssvc_sy_narg;
56283700Speter		sysent[SYS_nfssvc].sy_call = nfs_prev_nfssvc_sy_call;
56383700Speter		break;
56483700Speter	}
56583700Speter	return 0;
5661541Srgrimes}
56783700Speterstatic moduledata_t nfsserver_mod = {
56883700Speter	"nfsserver",
56983700Speter	nfsrv_modevent,
57083700Speter	NULL,
57183700Speter};
57283700SpeterDECLARE_MODULE(nfsserver, nfsserver_mod, SI_SUB_VFS, SI_ORDER_ANY);
5731541Srgrimes
57483700Speter/* So that loader and kldload(2) can find us, wherever we are.. */
57583700SpeterMODULE_VERSION(nfsserver, 1);
57638894Sbde
5771541Srgrimes/*
57827446Sdfr * Set up nameidata for a lookup() call and do it.
57927446Sdfr *
58027446Sdfr * If pubflag is set, this call is done for a lookup operation on the
58127446Sdfr * public filehandle. In that case we allow crossing mountpoints and
58227446Sdfr * absolute pathnames. However, the caller is expected to check that
58327446Sdfr * the lookup result is within the public fs, and deny access if
58427446Sdfr * it is not.
58548125Sjulian *
58648125Sjulian * nfs_namei() clears out garbage fields that namei() might leave garbage.
58748125Sjulian * This is mainly ni_vp and ni_dvp when an error occurs, and ni_dvp when no
58848125Sjulian * error occurs but the parent was not requested.
58948125Sjulian *
59083651Speter * dirp may be set whether an error is returned or not, and must be
59148125Sjulian * released by the caller.
5921541Srgrimes */
5931549Srgrimesint
59483651Speternfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
59583651Speter    struct nfssvc_sock *slp, struct sockaddr *nam, struct mbuf **mdp,
59683651Speter    caddr_t *dposp, struct vnode **retdirp, struct thread *td, int pubflag)
5971541Srgrimes{
59883651Speter	int i, rem;
59983651Speter	struct mbuf *md;
60083651Speter	char *fromcp, *tocp, *cp;
60127446Sdfr	struct iovec aiov;
60227446Sdfr	struct uio auio;
6031541Srgrimes	struct vnode *dp;
60427446Sdfr	int error, rdonly, linklen;
6051541Srgrimes	struct componentname *cnp = &ndp->ni_cnd;
6061541Srgrimes
6079336Sdfr	*retdirp = (struct vnode *)0;
60829653Sdyson	cnp->cn_pnbuf = zalloc(namei_zone);
60929653Sdyson
6101541Srgrimes	/*
6111541Srgrimes	 * Copy the name from the mbuf list to ndp->ni_pnbuf
6121541Srgrimes	 * and set the various ndp fields appropriately.
6131541Srgrimes	 */
6141541Srgrimes	fromcp = *dposp;
6151541Srgrimes	tocp = cnp->cn_pnbuf;
6161541Srgrimes	md = *mdp;
6171541Srgrimes	rem = mtod(md, caddr_t) + md->m_len - fromcp;
6181541Srgrimes	for (i = 0; i < len; i++) {
6191541Srgrimes		while (rem == 0) {
6201541Srgrimes			md = md->m_next;
6211541Srgrimes			if (md == NULL) {
6221541Srgrimes				error = EBADRPC;
6231541Srgrimes				goto out;
6241541Srgrimes			}
6251541Srgrimes			fromcp = mtod(md, caddr_t);
6261541Srgrimes			rem = md->m_len;
6271541Srgrimes		}
62827446Sdfr		if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) {
6299336Sdfr			error = EACCES;
6301541Srgrimes			goto out;
6311541Srgrimes		}
6321541Srgrimes		*tocp++ = *fromcp++;
6331541Srgrimes		rem--;
6341541Srgrimes	}
6351541Srgrimes	*tocp = '\0';
6361541Srgrimes	*mdp = md;
6371541Srgrimes	*dposp = fromcp;
6381541Srgrimes	len = nfsm_rndup(len)-len;
6391541Srgrimes	if (len > 0) {
6401541Srgrimes		if (rem >= len)
6411541Srgrimes			*dposp += len;
64227609Sdfr		else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
6439336Sdfr			goto out;
6441541Srgrimes	}
64527446Sdfr
6461541Srgrimes	/*
6471541Srgrimes	 * Extract and set starting directory.
6481541Srgrimes	 */
64927446Sdfr	error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
65083651Speter	    nam, &rdonly, pubflag);
65127446Sdfr	if (error)
6521541Srgrimes		goto out;
6531541Srgrimes	if (dp->v_type != VDIR) {
65417761Sdyson		vrele(dp);
6551541Srgrimes		error = ENOTDIR;
6561541Srgrimes		goto out;
6571541Srgrimes	}
65827446Sdfr
65927446Sdfr	if (rdonly)
66027446Sdfr		cnp->cn_flags |= RDONLY;
66127446Sdfr
66248125Sjulian	/*
66383651Speter	 * Set return directory.  Reference to dp is implicitly transfered
66448125Sjulian	 * to the returned pointer
66548125Sjulian	 */
66627609Sdfr	*retdirp = dp;
66727609Sdfr
66827446Sdfr	if (pubflag) {
66927446Sdfr		/*
67027446Sdfr		 * Oh joy. For WebNFS, handle those pesky '%' escapes,
67127446Sdfr		 * and the 'native path' indicator.
67227446Sdfr		 */
67329653Sdyson		cp = zalloc(namei_zone);
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;
69129653Sdyson				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;
70729653Sdyson					zfree(namei_zone, cp);
70827446Sdfr					goto out;
70927446Sdfr				}
71027446Sdfr			} else
71127446Sdfr				*tocp++ = *fromcp++;
71227446Sdfr		}
71327446Sdfr		*tocp = '\0';
71429653Sdyson		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;
72427446Sdfr		if (cnp->cn_pnbuf[0] == '/')
72527446Sdfr			dp = rootvnode;
72627446Sdfr	} else {
72727609Sdfr		cnp->cn_flags |= NOCROSSMOUNT;
72827446Sdfr	}
72927446Sdfr
73048125Sjulian	/*
73148125Sjulian	 * Initialize for scan, set ni_startdir and bump ref on dp again
73248125Sjulian	 * becuase lookup() will dereference ni_startdir.
73348125Sjulian	 */
73448125Sjulian
73583366Sjulian	cnp->cn_thread = td;
7369336Sdfr	VREF(dp);
73748125Sjulian	ndp->ni_startdir = dp;
73827446Sdfr
73948125Sjulian	for (;;) {
74048125Sjulian		cnp->cn_nameptr = cnp->cn_pnbuf;
74148125Sjulian		/*
74248125Sjulian		 * Call lookup() to do the real work.  If an error occurs,
74348125Sjulian		 * ndp->ni_vp and ni_dvp are left uninitialized or NULL and
74448125Sjulian		 * we do not have to dereference anything before returning.
74548125Sjulian		 * In either case ni_startdir will be dereferenced and NULLed
74648125Sjulian		 * out.
74748125Sjulian		 */
74848125Sjulian		error = lookup(ndp);
74948125Sjulian		if (error)
75048125Sjulian			break;
75148125Sjulian
75248125Sjulian		/*
75383651Speter		 * Check for encountering a symbolic link.  Trivial
75448125Sjulian		 * termination occurs if no symlink encountered.
75548125Sjulian		 * Note: zfree is safe because error is 0, so we will
75648125Sjulian		 * not zfree it again when we break.
75748125Sjulian		 */
75848125Sjulian		if ((cnp->cn_flags & ISSYMLINK) == 0) {
75948125Sjulian			nfsrv_object_create(ndp->ni_vp);
76048125Sjulian			if (cnp->cn_flags & (SAVENAME | SAVESTART))
76148125Sjulian				cnp->cn_flags |= HASBUF;
76248125Sjulian			else
76348125Sjulian				zfree(namei_zone, cnp->cn_pnbuf);
76448125Sjulian			break;
76527446Sdfr		}
76648125Sjulian
76748125Sjulian		/*
76848125Sjulian		 * Validate symlink
76948125Sjulian		 */
7701541Srgrimes		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
77183366Sjulian			VOP_UNLOCK(ndp->ni_dvp, 0, td);
77227446Sdfr		if (!pubflag) {
77327446Sdfr			error = EINVAL;
77448125Sjulian			goto badlink2;
77527446Sdfr		}
77627446Sdfr
77727446Sdfr		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
77827446Sdfr			error = ELOOP;
77948125Sjulian			goto badlink2;
78027446Sdfr		}
78127609Sdfr		if (ndp->ni_pathlen > 1)
78229653Sdyson			cp = zalloc(namei_zone);
7831541Srgrimes		else
78427446Sdfr			cp = cnp->cn_pnbuf;
78527446Sdfr		aiov.iov_base = cp;
78627446Sdfr		aiov.iov_len = MAXPATHLEN;
78727446Sdfr		auio.uio_iov = &aiov;
78827446Sdfr		auio.uio_iovcnt = 1;
78927446Sdfr		auio.uio_offset = 0;
79027446Sdfr		auio.uio_rw = UIO_READ;
79127446Sdfr		auio.uio_segflg = UIO_SYSSPACE;
79283366Sjulian		auio.uio_td = (struct thread *)0;
79327446Sdfr		auio.uio_resid = MAXPATHLEN;
79427446Sdfr		error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
79527446Sdfr		if (error) {
79648125Sjulian		badlink1:
79727446Sdfr			if (ndp->ni_pathlen > 1)
79829653Sdyson				zfree(namei_zone, cp);
79948125Sjulian		badlink2:
80048125Sjulian			vrele(ndp->ni_dvp);
80148125Sjulian			vput(ndp->ni_vp);
80227446Sdfr			break;
80327446Sdfr		}
80427446Sdfr		linklen = MAXPATHLEN - auio.uio_resid;
80527446Sdfr		if (linklen == 0) {
80627446Sdfr			error = ENOENT;
80748125Sjulian			goto badlink1;
80827446Sdfr		}
80927446Sdfr		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
81027446Sdfr			error = ENAMETOOLONG;
81148125Sjulian			goto badlink1;
81227446Sdfr		}
81348125Sjulian
81448125Sjulian		/*
81548125Sjulian		 * Adjust or replace path
81648125Sjulian		 */
81727446Sdfr		if (ndp->ni_pathlen > 1) {
81827446Sdfr			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
81929653Sdyson			zfree(namei_zone, cnp->cn_pnbuf);
82027446Sdfr			cnp->cn_pnbuf = cp;
82127446Sdfr		} else
82227446Sdfr			cnp->cn_pnbuf[linklen] = '\0';
82327446Sdfr		ndp->ni_pathlen += linklen;
82448125Sjulian
82527446Sdfr		/*
82683651Speter		 * Cleanup refs for next loop and check if root directory
82783651Speter		 * should replace current directory.  Normally ni_dvp
82848125Sjulian		 * becomes the new base directory and is cleaned up when
82948125Sjulian		 * we loop.  Explicitly null pointers after invalidation
83048125Sjulian		 * to clarify operation.
83127446Sdfr		 */
83248125Sjulian		vput(ndp->ni_vp);
83348125Sjulian		ndp->ni_vp = NULL;
83448125Sjulian
83527446Sdfr		if (cnp->cn_pnbuf[0] == '/') {
83648125Sjulian			vrele(ndp->ni_dvp);
83748125Sjulian			ndp->ni_dvp = ndp->ni_rootdir;
83848125Sjulian			VREF(ndp->ni_dvp);
83927446Sdfr		}
84048125Sjulian		ndp->ni_startdir = ndp->ni_dvp;
84148125Sjulian		ndp->ni_dvp = NULL;
8421541Srgrimes	}
84348125Sjulian
84448125Sjulian	/*
84548125Sjulian	 * nfs_namei() guarentees that fields will not contain garbage
84648125Sjulian	 * whether an error occurs or not.  This allows the caller to track
84748125Sjulian	 * cleanup state trivially.
84848125Sjulian	 */
8491541Srgrimesout:
85048125Sjulian	if (error) {
85148125Sjulian		zfree(namei_zone, cnp->cn_pnbuf);
85248125Sjulian		ndp->ni_vp = NULL;
85348125Sjulian		ndp->ni_dvp = NULL;
85448125Sjulian		ndp->ni_startdir = NULL;
85548125Sjulian		cnp->cn_flags &= ~HASBUF;
85648125Sjulian	} else if ((ndp->ni_cnd.cn_flags & (WANTPARENT|LOCKPARENT)) == 0) {
85748125Sjulian		ndp->ni_dvp = NULL;
85848125Sjulian	}
8591541Srgrimes	return (error);
8601541Srgrimes}
8611541Srgrimes
8621541Srgrimes/*
8631541Srgrimes * A fiddled version of m_adj() that ensures null fill to a long
8641541Srgrimes * boundary and only trims off the back end
8651541Srgrimes */
8661541Srgrimesvoid
86783651Speternfsm_adj(struct mbuf *mp, int len, int nul)
8681541Srgrimes{
86983651Speter	struct mbuf *m;
87083651Speter	int count, i;
87183651Speter	char *cp;
8721541Srgrimes
8731541Srgrimes	/*
8741541Srgrimes	 * Trim from tail.  Scan the mbuf chain,
8751541Srgrimes	 * calculating its length and finding the last mbuf.
8761541Srgrimes	 * If the adjustment only affects this mbuf, then just
8771541Srgrimes	 * adjust and return.  Otherwise, rescan and truncate
8781541Srgrimes	 * after the remaining size.
8791541Srgrimes	 */
8801541Srgrimes	count = 0;
8811541Srgrimes	m = mp;
8821541Srgrimes	for (;;) {
8831541Srgrimes		count += m->m_len;
8841541Srgrimes		if (m->m_next == (struct mbuf *)0)
8851541Srgrimes			break;
8861541Srgrimes		m = m->m_next;
8871541Srgrimes	}
8881541Srgrimes	if (m->m_len > len) {
8891541Srgrimes		m->m_len -= len;
8901541Srgrimes		if (nul > 0) {
8911541Srgrimes			cp = mtod(m, caddr_t)+m->m_len-nul;
8921541Srgrimes			for (i = 0; i < nul; i++)
8931541Srgrimes				*cp++ = '\0';
8941541Srgrimes		}
8951541Srgrimes		return;
8961541Srgrimes	}
8971541Srgrimes	count -= len;
8981541Srgrimes	if (count < 0)
8991541Srgrimes		count = 0;
9001541Srgrimes	/*
9011541Srgrimes	 * Correct length for chain is "count".
9021541Srgrimes	 * Find the mbuf with last data, adjust its length,
9031541Srgrimes	 * and toss data from remaining mbufs on chain.
9041541Srgrimes	 */
9051541Srgrimes	for (m = mp; m; m = m->m_next) {
9061541Srgrimes		if (m->m_len >= count) {
9071541Srgrimes			m->m_len = count;
9081541Srgrimes			if (nul > 0) {
9091541Srgrimes				cp = mtod(m, caddr_t)+m->m_len-nul;
9101541Srgrimes				for (i = 0; i < nul; i++)
9111541Srgrimes					*cp++ = '\0';
9121541Srgrimes			}
9131541Srgrimes			break;
9141541Srgrimes		}
9151541Srgrimes		count -= m->m_len;
9161541Srgrimes	}
9173305Sphk	for (m = m->m_next;m;m = m->m_next)
9181541Srgrimes		m->m_len = 0;
9191541Srgrimes}
9201541Srgrimes
9211541Srgrimes/*
9229336Sdfr * Make these functions instead of macros, so that the kernel text size
9239336Sdfr * doesn't get too big...
9249336Sdfr */
9259336Sdfrvoid
92683651Speternfsm_srvwcc(struct nfsrv_descript *nfsd, int before_ret,
92783651Speter    struct vattr *before_vap, int after_ret, struct vattr *after_vap,
92883651Speter    struct mbuf **mbp, char **bposp)
9299336Sdfr{
93083651Speter	struct mbuf *mb = *mbp;
93183651Speter	char *bpos = *bposp;
93283651Speter	u_int32_t *tl;
9339336Sdfr
9349336Sdfr	if (before_ret) {
93584002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
93689094Smsmith		*tl = nfsrv_nfs_false;
9379336Sdfr	} else {
93884002Speter		tl = nfsm_build(u_int32_t *, 7 * NFSX_UNSIGNED);
93989094Smsmith		*tl++ = nfsrv_nfs_true;
94047751Speter		txdr_hyper(before_vap->va_size, tl);
9419336Sdfr		tl += 2;
9429336Sdfr		txdr_nfsv3time(&(before_vap->va_mtime), tl);
9439336Sdfr		tl += 2;
9449336Sdfr		txdr_nfsv3time(&(before_vap->va_ctime), tl);
9459336Sdfr	}
9469336Sdfr	*bposp = bpos;
9479336Sdfr	*mbp = mb;
9489336Sdfr	nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp);
9499336Sdfr}
9509336Sdfr
9519336Sdfrvoid
95283651Speternfsm_srvpostopattr(struct nfsrv_descript *nfsd, int after_ret,
95383651Speter    struct vattr *after_vap, struct mbuf **mbp, char **bposp)
9549336Sdfr{
95583651Speter	struct mbuf *mb = *mbp;
95683651Speter	char *bpos = *bposp;
95783651Speter	u_int32_t *tl;
95883651Speter	struct nfs_fattr *fp;
9599336Sdfr
9609336Sdfr	if (after_ret) {
96184002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
96289094Smsmith		*tl = nfsrv_nfs_false;
9639336Sdfr	} else {
96484002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED + NFSX_V3FATTR);
96589094Smsmith		*tl++ = nfsrv_nfs_true;
9669336Sdfr		fp = (struct nfs_fattr *)tl;
9679336Sdfr		nfsm_srvfattr(nfsd, after_vap, fp);
9689336Sdfr	}
9699336Sdfr	*mbp = mb;
9709336Sdfr	*bposp = bpos;
9719336Sdfr}
9729336Sdfr
9739336Sdfrvoid
97483651Speternfsm_srvfattr(struct nfsrv_descript *nfsd, struct vattr *vap,
97583651Speter    struct nfs_fattr *fp)
9769336Sdfr{
9779336Sdfr
9789336Sdfr	fp->fa_nlink = txdr_unsigned(vap->va_nlink);
9799336Sdfr	fp->fa_uid = txdr_unsigned(vap->va_uid);
9809336Sdfr	fp->fa_gid = txdr_unsigned(vap->va_gid);
9819336Sdfr	if (nfsd->nd_flag & ND_NFSV3) {
9829336Sdfr		fp->fa_type = vtonfsv3_type(vap->va_type);
9839336Sdfr		fp->fa_mode = vtonfsv3_mode(vap->va_mode);
98447751Speter		txdr_hyper(vap->va_size, &fp->fa3_size);
98547751Speter		txdr_hyper(vap->va_bytes, &fp->fa3_used);
98647028Sphk		fp->fa3_rdev.specdata1 = txdr_unsigned(umajor(vap->va_rdev));
98747028Sphk		fp->fa3_rdev.specdata2 = txdr_unsigned(uminor(vap->va_rdev));
9889336Sdfr		fp->fa3_fsid.nfsuquad[0] = 0;
9899336Sdfr		fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
9909336Sdfr		fp->fa3_fileid.nfsuquad[0] = 0;
9919336Sdfr		fp->fa3_fileid.nfsuquad[1] = txdr_unsigned(vap->va_fileid);
9929336Sdfr		txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
9939336Sdfr		txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
9949336Sdfr		txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
9959336Sdfr	} else {
9969336Sdfr		fp->fa_type = vtonfsv2_type(vap->va_type);
9979336Sdfr		fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
9989336Sdfr		fp->fa2_size = txdr_unsigned(vap->va_size);
9999336Sdfr		fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
10009336Sdfr		if (vap->va_type == VFIFO)
10019336Sdfr			fp->fa2_rdev = 0xffffffff;
10029336Sdfr		else
10039336Sdfr			fp->fa2_rdev = txdr_unsigned(vap->va_rdev);
10049336Sdfr		fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
10059336Sdfr		fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
10069336Sdfr		fp->fa2_fileid = txdr_unsigned(vap->va_fileid);
10079336Sdfr		txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
10089336Sdfr		txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
10099336Sdfr		txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
10109336Sdfr	}
10119336Sdfr}
10129336Sdfr
10139336Sdfr/*
10141541Srgrimes * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
10151541Srgrimes * 	- look up fsid in mount list (if not found ret error)
10161541Srgrimes *	- get vp and export rights by calling VFS_FHTOVP()
10171541Srgrimes *	- if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
10181541Srgrimes *	- if not lockflag unlock it with VOP_UNLOCK()
10191541Srgrimes */
10201549Srgrimesint
102183651Speternfsrv_fhtovp(fhandle_t *fhp, int lockflag, struct vnode **vpp,
102283651Speter    struct ucred *cred, struct nfssvc_sock *slp, struct sockaddr *nam,
102383651Speter    int *rdonlyp, int pubflag)
10241541Srgrimes{
102583366Sjulian	struct thread *td = curthread; /* XXX */
102683651Speter	struct mount *mp;
102783651Speter	int i;
10281541Srgrimes	struct ucred *credanon;
10291541Srgrimes	int error, exflags;
103036534Speter#ifdef MNT_EXNORESPORT		/* XXX needs mountd and /etc/exports help yet */
103136534Speter	struct sockaddr_int *saddr;
103236534Speter#endif
10331541Srgrimes
10341541Srgrimes	*vpp = (struct vnode *)0;
103527446Sdfr
103627446Sdfr	if (nfs_ispublicfh(fhp)) {
103727446Sdfr		if (!pubflag || !nfs_pub.np_valid)
103827446Sdfr			return (ESTALE);
103927446Sdfr		fhp = &nfs_pub.np_handle;
104027446Sdfr	}
104127446Sdfr
104222521Sdyson	mp = vfs_getvfs(&fhp->fh_fsid);
10433305Sphk	if (!mp)
10441541Srgrimes		return (ESTALE);
104551138Salfred	error = VFS_CHECKEXP(mp, nam, &exflags, &credanon);
10463305Sphk	if (error)
104783651Speter		return (error);
104851138Salfred	error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp);
104951138Salfred	if (error)
10501541Srgrimes		return (error);
105136534Speter#ifdef MNT_EXNORESPORT
105236534Speter	if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) {
105336534Speter		saddr = (struct sockaddr_in *)nam;
105436534Speter		if (saddr->sin_family == AF_INET &&
105536534Speter		    ntohs(saddr->sin_port) >= IPPORT_RESERVED) {
105636534Speter			vput(*vpp);
105754485Sdillon			*vpp = NULL;
105836534Speter			return (NFSERR_AUTHERR | AUTH_TOOWEAK);
105936534Speter		}
106036534Speter	}
106136534Speter#endif
10621541Srgrimes	/*
10631541Srgrimes	 * Check/setup credentials.
10641541Srgrimes	 */
106583651Speter	if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
10661541Srgrimes		cred->cr_uid = credanon->cr_uid;
10671541Srgrimes		for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
10681541Srgrimes			cred->cr_groups[i] = credanon->cr_groups[i];
10693664Sphk		cred->cr_ngroups = i;
10701541Srgrimes	}
10711541Srgrimes	if (exflags & MNT_EXRDONLY)
10721541Srgrimes		*rdonlyp = 1;
10731541Srgrimes	else
10741541Srgrimes		*rdonlyp = 0;
10757969Sdyson
107617761Sdyson	nfsrv_object_create(*vpp);
10777969Sdyson
10781541Srgrimes	if (!lockflag)
107983366Sjulian		VOP_UNLOCK(*vpp, 0, td);
10801541Srgrimes	return (0);
10811541Srgrimes}
10821541Srgrimes
108327446Sdfr
108427446Sdfr/*
108527446Sdfr * WebNFS: check if a filehandle is a public filehandle. For v3, this
108627446Sdfr * means a length of 0, for v2 it means all zeroes. nfsm_srvmtofh has
108727446Sdfr * transformed this to all zeroes in both cases, so check for it.
108827446Sdfr */
108927446Sdfrint
109083651Speternfs_ispublicfh(fhandle_t *fhp)
109127446Sdfr{
109227446Sdfr	char *cp = (char *)fhp;
109327446Sdfr	int i;
109427446Sdfr
109527446Sdfr	for (i = 0; i < NFSX_V3FH; i++)
109627446Sdfr		if (*cp++ != 0)
109727446Sdfr			return (FALSE);
109827446Sdfr	return (TRUE);
109927446Sdfr}
110083651Speter
11011541Srgrimes/*
11021541Srgrimes * This function compares two net addresses by family and returns TRUE
11031541Srgrimes * if they are the same host.
11041541Srgrimes * If there is any doubt, return FALSE.
11051541Srgrimes * The AF_INET family is handled as a special case so that address mbufs
11061541Srgrimes * don't need to be saved to store "struct in_addr", which is only 4 bytes.
11071541Srgrimes */
11081549Srgrimesint
110983651Speternetaddr_match(int family, union nethostaddr *haddr, struct sockaddr *nam)
11101541Srgrimes{
111183651Speter	struct sockaddr_in *inetaddr;
11121541Srgrimes
11131541Srgrimes	switch (family) {
11141541Srgrimes	case AF_INET:
111528270Swollman		inetaddr = (struct sockaddr_in *)nam;
11161541Srgrimes		if (inetaddr->sin_family == AF_INET &&
11171541Srgrimes		    inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
11181541Srgrimes			return (1);
11191541Srgrimes		break;
11201541Srgrimes	default:
11211541Srgrimes		break;
11221541Srgrimes	};
11231541Srgrimes	return (0);
11241541Srgrimes}
11255455Sdg
11269336Sdfr/*
11279336Sdfr * Map errnos to NFS error numbers. For Version 3 also filter out error
11289336Sdfr * numbers not specified for the associated procedure.
11299336Sdfr */
11305455Sdgint
113183651Speternfsrv_errmap(struct nfsrv_descript *nd, int err)
11329336Sdfr{
113383651Speter	short *defaulterrp, *errp;
11349336Sdfr
11359336Sdfr	if (nd->nd_flag & ND_NFSV3) {
11369336Sdfr	    if (nd->nd_procnum <= NFSPROC_COMMIT) {
11379336Sdfr		errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
11389336Sdfr		while (*++errp) {
11399336Sdfr			if (*errp == err)
11409336Sdfr				return (err);
11419336Sdfr			else if (*errp > err)
11429336Sdfr				break;
11439336Sdfr		}
11449336Sdfr		return ((int)*defaulterrp);
11459336Sdfr	    } else
11469336Sdfr		return (err & 0xffff);
11479336Sdfr	}
11489336Sdfr	if (err <= ELAST)
11499336Sdfr		return ((int)nfsrv_v2errmap[err - 1]);
11509336Sdfr	return (NFSERR_IO);
11519336Sdfr}
11529336Sdfr
11539336Sdfrint
115483651Speternfsrv_object_create(struct vnode *vp)
115531886Sbde{
11565455Sdg
115731886Sbde	if (vp == NULL || vp->v_type != VREG)
115831886Sbde		return (1);
115983366Sjulian	return (vfs_object_create(vp, curthread,
116083366Sjulian			  curthread ? curthread->td_proc->p_ucred : NULL));
11615455Sdg}
116236503Speter
116336503Speter/*
116436503Speter * Sort the group list in increasing numerical order.
116536503Speter * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
116636503Speter *  that used to be here.)
116736503Speter */
116836503Spetervoid
116983651Speternfsrvw_sort(gid_t *list, int num)
117036503Speter{
117183651Speter	int i, j;
117236503Speter	gid_t v;
117336503Speter
117436503Speter	/* Insertion sort. */
117536503Speter	for (i = 1; i < num; i++) {
117636503Speter		v = list[i];
117736503Speter		/* find correct slot for value v, moving others up */
117836503Speter		for (j = i; --j >= 0 && v < list[j];)
117936503Speter			list[j + 1] = list[j];
118036503Speter		list[j + 1] = v;
118136503Speter	}
118236503Speter}
118336503Speter
118436503Speter/*
118536503Speter * copy credentials making sure that the result can be compared with bcmp().
118636503Speter */
118736503Spetervoid
118883651Speternfsrv_setcred(struct ucred *incred, struct ucred *outcred)
118936503Speter{
119083651Speter	int i;
119136503Speter
119236503Speter	bzero((caddr_t)outcred, sizeof (struct ucred));
119336503Speter	outcred->cr_ref = 1;
119436503Speter	outcred->cr_uid = incred->cr_uid;
119536503Speter	outcred->cr_ngroups = incred->cr_ngroups;
119636503Speter	for (i = 0; i < incred->cr_ngroups; i++)
119736503Speter		outcred->cr_groups[i] = incred->cr_groups[i];
119836503Speter	nfsrvw_sort(outcred->cr_groups, outcred->cr_ngroups);
119936503Speter}
120083651Speter
120183651Speter/*
120283651Speter * Helper functions for macros.
120383651Speter */
120483651Speter
120583651Spetervoid
120688091Siedowsenfsm_srvfhtom_xx(fhandle_t *f, int v3, struct mbuf **mb, caddr_t *bpos)
120783651Speter{
120888091Siedowse	u_int32_t *tl;
120983651Speter
121083651Speter	if (v3) {
121188091Siedowse		tl = nfsm_build_xx(NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
121288091Siedowse		*tl++ = txdr_unsigned(NFSX_V3FH);
121388091Siedowse		bcopy(f, tl, NFSX_V3FH);
121483651Speter	} else {
121588091Siedowse		tl = nfsm_build_xx(NFSX_V2FH, mb, bpos);
121688091Siedowse		bcopy(f, tl, NFSX_V2FH);
121783651Speter	}
121883651Speter}
121983651Speter
122083651Spetervoid
122188091Siedowsenfsm_srvpostop_fh_xx(fhandle_t *f, struct mbuf **mb, caddr_t *bpos)
122283651Speter{
122388091Siedowse	u_int32_t *tl;
122484002Speter
122588091Siedowse	tl = nfsm_build_xx(2 * NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
122689094Smsmith	*tl++ = nfsrv_nfs_true;
122788091Siedowse	*tl++ = txdr_unsigned(NFSX_V3FH);
122888091Siedowse	bcopy(f, tl, NFSX_V3FH);
122983651Speter}
123083651Speter
123183651Speterint
123288091Siedowsenfsm_srvstrsiz_xx(int *s, int m, struct mbuf **md, caddr_t *dpos)
123383651Speter{
123488091Siedowse	u_int32_t *tl;
123583651Speter
123688091Siedowse	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
123788091Siedowse	if (tl == NULL)
123884057Speter		return EBADRPC;
123988091Siedowse	*s = fxdr_unsigned(int32_t, *tl);
124083651Speter	if (*s > m || *s <= 0)
124183651Speter		return EBADRPC;
124283651Speter	return 0;
124383651Speter}
124483651Speter
124583651Speterint
124688091Siedowsenfsm_srvnamesiz_xx(int *s, struct mbuf **md, caddr_t *dpos)
124783651Speter{
124888091Siedowse	u_int32_t *tl;
124983651Speter
125088091Siedowse	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
125188091Siedowse	if (tl == NULL)
125284057Speter		return EBADRPC;
125388091Siedowse	*s = fxdr_unsigned(int32_t, *tl);
125483651Speter	if (*s > NFS_MAXNAMLEN)
125583651Speter		return NFSERR_NAMETOL;
125683651Speter	if (*s <= 0)
125783651Speter		return EBADRPC;
125883651Speter	return 0;
125983651Speter}
126083651Speter
126183651Spetervoid
126283651Speternfsm_clget_xx(u_int32_t **tl, struct mbuf *mb, struct mbuf **mp,
126383651Speter    char **bp, char **be, caddr_t bpos)
126483651Speter{
126583651Speter	struct mbuf *nmp;
126683651Speter
126783651Speter	if (*bp >= *be) {
126883651Speter		if (*mp == mb)
126983651Speter			(*mp)->m_len += *bp - bpos;
127083651Speter		MGET(nmp, M_TRYWAIT, MT_DATA);
127183651Speter		MCLGET(nmp, M_TRYWAIT);
127283651Speter		nmp->m_len = NFSMSIZ(nmp);
127383651Speter		(*mp)->m_next = nmp;
127483651Speter		*mp = nmp;
127583651Speter		*bp = mtod(*mp, caddr_t);
127683651Speter		*be = *bp + (*mp)->m_len;
127783651Speter	}
127883651Speter	*tl = (u_int32_t *)*bp;
127983651Speter}
128083651Speter
128183651Speterint
128288091Siedowsenfsm_srvmtofh_xx(fhandle_t *f, struct nfsrv_descript *nfsd, struct mbuf **md,
128388091Siedowse    caddr_t *dpos)
128483651Speter{
128588091Siedowse	u_int32_t *tl;
128683651Speter	int fhlen;
128783651Speter
128883651Speter	if (nfsd->nd_flag & ND_NFSV3) {
128988091Siedowse		tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
129088091Siedowse		if (tl == NULL)
129184057Speter			return EBADRPC;
129288091Siedowse		fhlen = fxdr_unsigned(int, *tl);
129383651Speter		if (fhlen != 0 && fhlen != NFSX_V3FH)
129483651Speter			return EBADRPC;
129583651Speter	} else {
129683651Speter		fhlen = NFSX_V2FH;
129783651Speter	}
129883651Speter	if (fhlen != 0) {
129988091Siedowse		tl = nfsm_dissect_xx(fhlen, md, dpos);
130088091Siedowse		if (tl == NULL)
130184057Speter			return EBADRPC;
130288091Siedowse		bcopy((caddr_t)tl, (caddr_t)(f), fhlen);
130383651Speter	} else {
130483651Speter		bzero((caddr_t)(f), NFSX_V3FH);
130583651Speter	}
130683651Speter	return 0;
130783651Speter}
130883651Speter
130983651Speterint
131088091Siedowsenfsm_srvsattr_xx(struct vattr *a, struct mbuf **md, caddr_t *dpos)
131183651Speter{
131288091Siedowse	u_int32_t *tl;
131383651Speter
131488091Siedowse	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
131588091Siedowse	if (tl == NULL)
131684057Speter		return EBADRPC;
131789094Smsmith	if (*tl == nfsrv_nfs_true) {
131888091Siedowse		tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
131988091Siedowse		if (tl == NULL)
132084057Speter			return EBADRPC;
132188091Siedowse		(a)->va_mode = nfstov_mode(*tl);
132283651Speter	}
132388091Siedowse	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
132488091Siedowse	if (tl == NULL)
132584057Speter		return EBADRPC;
132689094Smsmith	if (*tl == nfsrv_nfs_true) {
132788091Siedowse		tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
132888091Siedowse		if (tl == NULL)
132984057Speter			return EBADRPC;
133088091Siedowse		(a)->va_uid = fxdr_unsigned(uid_t, *tl);
133183651Speter	}
133288091Siedowse	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
133388091Siedowse	if (tl == NULL)
133484057Speter		return EBADRPC;
133589094Smsmith	if (*tl == nfsrv_nfs_true) {
133688091Siedowse		tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
133788091Siedowse		if (tl == NULL)
133884057Speter			return EBADRPC;
133988091Siedowse		(a)->va_gid = fxdr_unsigned(gid_t, *tl);
134083651Speter	}
134188091Siedowse	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
134288091Siedowse	if (tl == NULL)
134384057Speter		return EBADRPC;
134489094Smsmith	if (*tl == nfsrv_nfs_true) {
134588091Siedowse		tl = nfsm_dissect_xx(2 * NFSX_UNSIGNED, md, dpos);
134688091Siedowse		if (tl == NULL)
134784057Speter			return EBADRPC;
134888091Siedowse		(a)->va_size = fxdr_hyper(tl);
134983651Speter	}
135088091Siedowse	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
135188091Siedowse	if (tl == NULL)
135284057Speter		return EBADRPC;
135388091Siedowse	switch (fxdr_unsigned(int, *tl)) {
135483651Speter	case NFSV3SATTRTIME_TOCLIENT:
135588091Siedowse		tl = nfsm_dissect_xx(2 * NFSX_UNSIGNED, md, dpos);
135688091Siedowse		if (tl == NULL)
135784057Speter			return EBADRPC;
135888091Siedowse		fxdr_nfsv3time(tl, &(a)->va_atime);
135983651Speter		break;
136083651Speter	case NFSV3SATTRTIME_TOSERVER:
136183651Speter		getnanotime(&(a)->va_atime);
136283651Speter		break;
136383651Speter	}
136488091Siedowse	tl = nfsm_dissect_xx(NFSX_UNSIGNED, md, dpos);
136588091Siedowse	if (tl == NULL)
136684057Speter		return EBADRPC;
136788091Siedowse	switch (fxdr_unsigned(int, *tl)) {
136883651Speter	case NFSV3SATTRTIME_TOCLIENT:
136988091Siedowse		tl = nfsm_dissect_xx(2 * NFSX_UNSIGNED, md, dpos);
137088091Siedowse		if (tl == NULL)
137184057Speter			return EBADRPC;
137288091Siedowse		fxdr_nfsv3time(tl, &(a)->va_mtime);
137383651Speter		break;
137483651Speter	case NFSV3SATTRTIME_TOSERVER:
137583651Speter		getnanotime(&(a)->va_mtime);
137683651Speter		break;
137783651Speter	}
137883651Speter	return 0;
137983651Speter}
1380