nfs_srvsubs.c revision 84002
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 84002 2001-09-27 02:33:36Z peter $
381541Srgrimes */
391541Srgrimes
4083651Speter#include <sys/cdefs.h>
4183651Speter__FBSDID("$FreeBSD: head/sys/nfsserver/nfs_srvsubs.c 84002 2001-09-27 02:33:36Z peter $");
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 */
8436541Speteru_int32_t nfs_xdrneg1;
8536541Speteru_int32_t rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
8683651Speter	rpc_mismatch, rpc_auth_unix, rpc_msgaccepted;
8783651Speteru_int32_t nfs_prog, nfs_true, nfs_false;
881541Srgrimes
891541Srgrimes/* And other global data */
9083651Speterstatic nfstype nfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK,
9183651Speter				 NFNON, NFCHR, NFNON };
9283651Speter#define vtonfsv2_type(a)	txdr_unsigned(nfsv2_type[((int32_t)(a))])
9383651Speter#define vtonfsv3_mode(m)	txdr_unsigned((m) & ALLPERMS)
9412911Sphk
959336Sdfrint nfs_ticks;
969336Sdfr
979759Sbdestruct nfssvc_sockhead nfssvc_sockhead;
989759Sbdeint nfssvc_sockhead_flag;
999759Sbdestruct nfsd_head nfsd_head;
1009759Sbdeint nfsd_head_flag;
1019759Sbde
10238894Sbdestatic int nfs_prev_nfssvc_sy_narg;
10338894Sbdestatic sy_call_t *nfs_prev_nfssvc_sy_call;
10438894Sbde
1059336Sdfr/*
1069336Sdfr * Mapping of old NFS Version 2 RPC numbers to generic numbers.
1079336Sdfr */
1089336Sdfrint nfsv3_procid[NFS_NPROCS] = {
1099336Sdfr	NFSPROC_NULL,
1109336Sdfr	NFSPROC_GETATTR,
1119336Sdfr	NFSPROC_SETATTR,
1129336Sdfr	NFSPROC_NOOP,
1139336Sdfr	NFSPROC_LOOKUP,
1149336Sdfr	NFSPROC_READLINK,
1159336Sdfr	NFSPROC_READ,
1169336Sdfr	NFSPROC_NOOP,
1179336Sdfr	NFSPROC_WRITE,
1189336Sdfr	NFSPROC_CREATE,
1199336Sdfr	NFSPROC_REMOVE,
1209336Sdfr	NFSPROC_RENAME,
1219336Sdfr	NFSPROC_LINK,
1229336Sdfr	NFSPROC_SYMLINK,
1239336Sdfr	NFSPROC_MKDIR,
1249336Sdfr	NFSPROC_RMDIR,
1259336Sdfr	NFSPROC_READDIR,
1269336Sdfr	NFSPROC_FSSTAT,
1279336Sdfr	NFSPROC_NOOP,
1289336Sdfr	NFSPROC_NOOP,
1299336Sdfr	NFSPROC_NOOP,
1309336Sdfr	NFSPROC_NOOP,
1319336Sdfr	NFSPROC_NOOP,
1329336Sdfr};
1339336Sdfr
1349336Sdfr/*
1359336Sdfr * and the reverse mapping from generic to Version 2 procedure numbers
1369336Sdfr */
13783651Speterint nfsrvv2_procid[NFS_NPROCS] = {
1389336Sdfr	NFSV2PROC_NULL,
1399336Sdfr	NFSV2PROC_GETATTR,
1409336Sdfr	NFSV2PROC_SETATTR,
1419336Sdfr	NFSV2PROC_LOOKUP,
1429336Sdfr	NFSV2PROC_NOOP,
1439336Sdfr	NFSV2PROC_READLINK,
1449336Sdfr	NFSV2PROC_READ,
1459336Sdfr	NFSV2PROC_WRITE,
1469336Sdfr	NFSV2PROC_CREATE,
1479336Sdfr	NFSV2PROC_MKDIR,
1489336Sdfr	NFSV2PROC_SYMLINK,
1499336Sdfr	NFSV2PROC_CREATE,
1509336Sdfr	NFSV2PROC_REMOVE,
1519336Sdfr	NFSV2PROC_RMDIR,
1529336Sdfr	NFSV2PROC_RENAME,
1539336Sdfr	NFSV2PROC_LINK,
1549336Sdfr	NFSV2PROC_READDIR,
1559336Sdfr	NFSV2PROC_NOOP,
1569336Sdfr	NFSV2PROC_STATFS,
1579336Sdfr	NFSV2PROC_NOOP,
1589336Sdfr	NFSV2PROC_NOOP,
1599336Sdfr	NFSV2PROC_NOOP,
1609336Sdfr	NFSV2PROC_NOOP,
1619336Sdfr};
1629336Sdfr
1639336Sdfr/*
1649336Sdfr * Maps errno values to nfs error numbers.
1659336Sdfr * Use NFSERR_IO as the catch all for ones not specifically defined in
1669336Sdfr * RFC 1094.
1679336Sdfr */
1689336Sdfrstatic u_char nfsrv_v2errmap[ELAST] = {
1699336Sdfr  NFSERR_PERM,	NFSERR_NOENT,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1709336Sdfr  NFSERR_NXIO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1719336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_ACCES,	NFSERR_IO,	NFSERR_IO,
1729336Sdfr  NFSERR_IO,	NFSERR_EXIST,	NFSERR_IO,	NFSERR_NODEV,	NFSERR_NOTDIR,
1739336Sdfr  NFSERR_ISDIR,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1749336Sdfr  NFSERR_IO,	NFSERR_FBIG,	NFSERR_NOSPC,	NFSERR_IO,	NFSERR_ROFS,
1759336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
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_NAMETOL,	NFSERR_IO,	NFSERR_IO,
1829336Sdfr  NFSERR_NOTEMPTY, NFSERR_IO,	NFSERR_IO,	NFSERR_DQUOT,	NFSERR_STALE,
1839336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
1849336Sdfr  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
18541796Sdt  NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,	NFSERR_IO,
18641796Sdt  NFSERR_IO /* << Last is 86 */
1879336Sdfr};
1889336Sdfr
1899336Sdfr/*
1909336Sdfr * Maps errno values to nfs error numbers.
1919336Sdfr * Although it is not obvious whether or not NFS clients really care if
1929336Sdfr * a returned error value is in the specified list for the procedure, the
1939336Sdfr * safest thing to do is filter them appropriately. For Version 2, the
1949336Sdfr * X/Open XNFS document is the only specification that defines error values
1959336Sdfr * for each RPC (The RFC simply lists all possible error values for all RPCs),
1969336Sdfr * so I have decided to not do this for Version 2.
1979336Sdfr * The first entry is the default error return and the rest are the valid
1989336Sdfr * errors for that RPC in increasing numeric order.
1999336Sdfr */
2009336Sdfrstatic short nfsv3err_null[] = {
2019336Sdfr	0,
2029336Sdfr	0,
2039336Sdfr};
2049336Sdfr
2059336Sdfrstatic short nfsv3err_getattr[] = {
2069336Sdfr	NFSERR_IO,
2079336Sdfr	NFSERR_IO,
2089336Sdfr	NFSERR_STALE,
2099336Sdfr	NFSERR_BADHANDLE,
2109336Sdfr	NFSERR_SERVERFAULT,
2119336Sdfr	0,
2129336Sdfr};
2139336Sdfr
2149336Sdfrstatic short nfsv3err_setattr[] = {
2159336Sdfr	NFSERR_IO,
2169336Sdfr	NFSERR_PERM,
2179336Sdfr	NFSERR_IO,
2189336Sdfr	NFSERR_ACCES,
2199336Sdfr	NFSERR_INVAL,
2209336Sdfr	NFSERR_NOSPC,
2219336Sdfr	NFSERR_ROFS,
2229336Sdfr	NFSERR_DQUOT,
2239336Sdfr	NFSERR_STALE,
2249336Sdfr	NFSERR_BADHANDLE,
2259336Sdfr	NFSERR_NOT_SYNC,
2269336Sdfr	NFSERR_SERVERFAULT,
2279336Sdfr	0,
2289336Sdfr};
2299336Sdfr
2309336Sdfrstatic short nfsv3err_lookup[] = {
2319336Sdfr	NFSERR_IO,
2329336Sdfr	NFSERR_NOENT,
2339336Sdfr	NFSERR_IO,
2349336Sdfr	NFSERR_ACCES,
2359336Sdfr	NFSERR_NOTDIR,
2369336Sdfr	NFSERR_NAMETOL,
2379336Sdfr	NFSERR_STALE,
2389336Sdfr	NFSERR_BADHANDLE,
2399336Sdfr	NFSERR_SERVERFAULT,
2409336Sdfr	0,
2419336Sdfr};
2429336Sdfr
2439336Sdfrstatic short nfsv3err_access[] = {
2449336Sdfr	NFSERR_IO,
2459336Sdfr	NFSERR_IO,
2469336Sdfr	NFSERR_STALE,
2479336Sdfr	NFSERR_BADHANDLE,
2489336Sdfr	NFSERR_SERVERFAULT,
2499336Sdfr	0,
2509336Sdfr};
2519336Sdfr
2529336Sdfrstatic short nfsv3err_readlink[] = {
2539336Sdfr	NFSERR_IO,
2549336Sdfr	NFSERR_IO,
2559336Sdfr	NFSERR_ACCES,
2569336Sdfr	NFSERR_INVAL,
2579336Sdfr	NFSERR_STALE,
2589336Sdfr	NFSERR_BADHANDLE,
2599336Sdfr	NFSERR_NOTSUPP,
2609336Sdfr	NFSERR_SERVERFAULT,
2619336Sdfr	0,
2629336Sdfr};
2639336Sdfr
2649336Sdfrstatic short nfsv3err_read[] = {
2659336Sdfr	NFSERR_IO,
2669336Sdfr	NFSERR_IO,
2679336Sdfr	NFSERR_NXIO,
2689336Sdfr	NFSERR_ACCES,
2699336Sdfr	NFSERR_INVAL,
2709336Sdfr	NFSERR_STALE,
2719336Sdfr	NFSERR_BADHANDLE,
2729336Sdfr	NFSERR_SERVERFAULT,
2739336Sdfr	0,
2749336Sdfr};
2759336Sdfr
2769336Sdfrstatic short nfsv3err_write[] = {
2779336Sdfr	NFSERR_IO,
2789336Sdfr	NFSERR_IO,
2799336Sdfr	NFSERR_ACCES,
2809336Sdfr	NFSERR_INVAL,
2819336Sdfr	NFSERR_FBIG,
2829336Sdfr	NFSERR_NOSPC,
2839336Sdfr	NFSERR_ROFS,
2849336Sdfr	NFSERR_DQUOT,
2859336Sdfr	NFSERR_STALE,
2869336Sdfr	NFSERR_BADHANDLE,
2879336Sdfr	NFSERR_SERVERFAULT,
2889336Sdfr	0,
2899336Sdfr};
2909336Sdfr
2919336Sdfrstatic short nfsv3err_create[] = {
2929336Sdfr	NFSERR_IO,
2939336Sdfr	NFSERR_IO,
2949336Sdfr	NFSERR_ACCES,
2959336Sdfr	NFSERR_EXIST,
2969336Sdfr	NFSERR_NOTDIR,
2979336Sdfr	NFSERR_NOSPC,
2989336Sdfr	NFSERR_ROFS,
2999336Sdfr	NFSERR_NAMETOL,
3009336Sdfr	NFSERR_DQUOT,
3019336Sdfr	NFSERR_STALE,
3029336Sdfr	NFSERR_BADHANDLE,
3039336Sdfr	NFSERR_NOTSUPP,
3049336Sdfr	NFSERR_SERVERFAULT,
3059336Sdfr	0,
3069336Sdfr};
3079336Sdfr
3089336Sdfrstatic short nfsv3err_mkdir[] = {
3099336Sdfr	NFSERR_IO,
3109336Sdfr	NFSERR_IO,
3119336Sdfr	NFSERR_ACCES,
3129336Sdfr	NFSERR_EXIST,
3139336Sdfr	NFSERR_NOTDIR,
3149336Sdfr	NFSERR_NOSPC,
3159336Sdfr	NFSERR_ROFS,
3169336Sdfr	NFSERR_NAMETOL,
3179336Sdfr	NFSERR_DQUOT,
3189336Sdfr	NFSERR_STALE,
3199336Sdfr	NFSERR_BADHANDLE,
3209336Sdfr	NFSERR_NOTSUPP,
3219336Sdfr	NFSERR_SERVERFAULT,
3229336Sdfr	0,
3239336Sdfr};
3249336Sdfr
3259336Sdfrstatic short nfsv3err_symlink[] = {
3269336Sdfr	NFSERR_IO,
3279336Sdfr	NFSERR_IO,
3289336Sdfr	NFSERR_ACCES,
3299336Sdfr	NFSERR_EXIST,
3309336Sdfr	NFSERR_NOTDIR,
3319336Sdfr	NFSERR_NOSPC,
3329336Sdfr	NFSERR_ROFS,
3339336Sdfr	NFSERR_NAMETOL,
3349336Sdfr	NFSERR_DQUOT,
3359336Sdfr	NFSERR_STALE,
3369336Sdfr	NFSERR_BADHANDLE,
3379336Sdfr	NFSERR_NOTSUPP,
3389336Sdfr	NFSERR_SERVERFAULT,
3399336Sdfr	0,
3409336Sdfr};
3419336Sdfr
3429336Sdfrstatic short nfsv3err_mknod[] = {
3439336Sdfr	NFSERR_IO,
3449336Sdfr	NFSERR_IO,
3459336Sdfr	NFSERR_ACCES,
3469336Sdfr	NFSERR_EXIST,
3479336Sdfr	NFSERR_NOTDIR,
3489336Sdfr	NFSERR_NOSPC,
3499336Sdfr	NFSERR_ROFS,
3509336Sdfr	NFSERR_NAMETOL,
3519336Sdfr	NFSERR_DQUOT,
3529336Sdfr	NFSERR_STALE,
3539336Sdfr	NFSERR_BADHANDLE,
3549336Sdfr	NFSERR_NOTSUPP,
3559336Sdfr	NFSERR_SERVERFAULT,
3569336Sdfr	NFSERR_BADTYPE,
3579336Sdfr	0,
3589336Sdfr};
3599336Sdfr
3609336Sdfrstatic short nfsv3err_remove[] = {
3619336Sdfr	NFSERR_IO,
3629336Sdfr	NFSERR_NOENT,
3639336Sdfr	NFSERR_IO,
3649336Sdfr	NFSERR_ACCES,
3659336Sdfr	NFSERR_NOTDIR,
3669336Sdfr	NFSERR_ROFS,
3679336Sdfr	NFSERR_NAMETOL,
3689336Sdfr	NFSERR_STALE,
3699336Sdfr	NFSERR_BADHANDLE,
3709336Sdfr	NFSERR_SERVERFAULT,
3719336Sdfr	0,
3729336Sdfr};
3739336Sdfr
3749336Sdfrstatic short nfsv3err_rmdir[] = {
3759336Sdfr	NFSERR_IO,
3769336Sdfr	NFSERR_NOENT,
3779336Sdfr	NFSERR_IO,
3789336Sdfr	NFSERR_ACCES,
3799336Sdfr	NFSERR_EXIST,
3809336Sdfr	NFSERR_NOTDIR,
3819336Sdfr	NFSERR_INVAL,
3829336Sdfr	NFSERR_ROFS,
3839336Sdfr	NFSERR_NAMETOL,
3849336Sdfr	NFSERR_NOTEMPTY,
3859336Sdfr	NFSERR_STALE,
3869336Sdfr	NFSERR_BADHANDLE,
3879336Sdfr	NFSERR_NOTSUPP,
3889336Sdfr	NFSERR_SERVERFAULT,
3899336Sdfr	0,
3909336Sdfr};
3919336Sdfr
3929336Sdfrstatic short nfsv3err_rename[] = {
3939336Sdfr	NFSERR_IO,
3949336Sdfr	NFSERR_NOENT,
3959336Sdfr	NFSERR_IO,
3969336Sdfr	NFSERR_ACCES,
3979336Sdfr	NFSERR_EXIST,
3989336Sdfr	NFSERR_XDEV,
3999336Sdfr	NFSERR_NOTDIR,
4009336Sdfr	NFSERR_ISDIR,
4019336Sdfr	NFSERR_INVAL,
4029336Sdfr	NFSERR_NOSPC,
4039336Sdfr	NFSERR_ROFS,
4049336Sdfr	NFSERR_MLINK,
4059336Sdfr	NFSERR_NAMETOL,
4069336Sdfr	NFSERR_NOTEMPTY,
4079336Sdfr	NFSERR_DQUOT,
4089336Sdfr	NFSERR_STALE,
4099336Sdfr	NFSERR_BADHANDLE,
4109336Sdfr	NFSERR_NOTSUPP,
4119336Sdfr	NFSERR_SERVERFAULT,
4129336Sdfr	0,
4139336Sdfr};
4149336Sdfr
4159336Sdfrstatic short nfsv3err_link[] = {
4169336Sdfr	NFSERR_IO,
4179336Sdfr	NFSERR_IO,
4189336Sdfr	NFSERR_ACCES,
4199336Sdfr	NFSERR_EXIST,
4209336Sdfr	NFSERR_XDEV,
4219336Sdfr	NFSERR_NOTDIR,
4229336Sdfr	NFSERR_INVAL,
4239336Sdfr	NFSERR_NOSPC,
4249336Sdfr	NFSERR_ROFS,
4259336Sdfr	NFSERR_MLINK,
4269336Sdfr	NFSERR_NAMETOL,
4279336Sdfr	NFSERR_DQUOT,
4289336Sdfr	NFSERR_STALE,
4299336Sdfr	NFSERR_BADHANDLE,
4309336Sdfr	NFSERR_NOTSUPP,
4319336Sdfr	NFSERR_SERVERFAULT,
4329336Sdfr	0,
4339336Sdfr};
4349336Sdfr
4359336Sdfrstatic short nfsv3err_readdir[] = {
4369336Sdfr	NFSERR_IO,
4379336Sdfr	NFSERR_IO,
4389336Sdfr	NFSERR_ACCES,
4399336Sdfr	NFSERR_NOTDIR,
4409336Sdfr	NFSERR_STALE,
4419336Sdfr	NFSERR_BADHANDLE,
4429336Sdfr	NFSERR_BAD_COOKIE,
4439336Sdfr	NFSERR_TOOSMALL,
4449336Sdfr	NFSERR_SERVERFAULT,
4459336Sdfr	0,
4469336Sdfr};
4479336Sdfr
4489336Sdfrstatic short nfsv3err_readdirplus[] = {
4499336Sdfr	NFSERR_IO,
4509336Sdfr	NFSERR_IO,
4519336Sdfr	NFSERR_ACCES,
4529336Sdfr	NFSERR_NOTDIR,
4539336Sdfr	NFSERR_STALE,
4549336Sdfr	NFSERR_BADHANDLE,
4559336Sdfr	NFSERR_BAD_COOKIE,
4569336Sdfr	NFSERR_NOTSUPP,
4579336Sdfr	NFSERR_TOOSMALL,
4589336Sdfr	NFSERR_SERVERFAULT,
4599336Sdfr	0,
4609336Sdfr};
4619336Sdfr
4629336Sdfrstatic short nfsv3err_fsstat[] = {
4639336Sdfr	NFSERR_IO,
4649336Sdfr	NFSERR_IO,
4659336Sdfr	NFSERR_STALE,
4669336Sdfr	NFSERR_BADHANDLE,
4679336Sdfr	NFSERR_SERVERFAULT,
4689336Sdfr	0,
4699336Sdfr};
4709336Sdfr
4719336Sdfrstatic short nfsv3err_fsinfo[] = {
4729336Sdfr	NFSERR_STALE,
4739336Sdfr	NFSERR_STALE,
4749336Sdfr	NFSERR_BADHANDLE,
4759336Sdfr	NFSERR_SERVERFAULT,
4769336Sdfr	0,
4779336Sdfr};
4789336Sdfr
4799336Sdfrstatic short nfsv3err_pathconf[] = {
4809336Sdfr	NFSERR_STALE,
4819336Sdfr	NFSERR_STALE,
4829336Sdfr	NFSERR_BADHANDLE,
4839336Sdfr	NFSERR_SERVERFAULT,
4849336Sdfr	0,
4859336Sdfr};
4869336Sdfr
4879336Sdfrstatic short nfsv3err_commit[] = {
4889336Sdfr	NFSERR_IO,
4899336Sdfr	NFSERR_IO,
4909336Sdfr	NFSERR_STALE,
4919336Sdfr	NFSERR_BADHANDLE,
4929336Sdfr	NFSERR_SERVERFAULT,
4939336Sdfr	0,
4949336Sdfr};
4959336Sdfr
4969336Sdfrstatic short *nfsrv_v3errmap[] = {
4979336Sdfr	nfsv3err_null,
4989336Sdfr	nfsv3err_getattr,
4999336Sdfr	nfsv3err_setattr,
5009336Sdfr	nfsv3err_lookup,
5019336Sdfr	nfsv3err_access,
5029336Sdfr	nfsv3err_readlink,
5039336Sdfr	nfsv3err_read,
5049336Sdfr	nfsv3err_write,
5059336Sdfr	nfsv3err_create,
5069336Sdfr	nfsv3err_mkdir,
5079336Sdfr	nfsv3err_symlink,
5089336Sdfr	nfsv3err_mknod,
5099336Sdfr	nfsv3err_remove,
5109336Sdfr	nfsv3err_rmdir,
5119336Sdfr	nfsv3err_rename,
5129336Sdfr	nfsv3err_link,
5139336Sdfr	nfsv3err_readdir,
5149336Sdfr	nfsv3err_readdirplus,
5159336Sdfr	nfsv3err_fsstat,
5169336Sdfr	nfsv3err_fsinfo,
5179336Sdfr	nfsv3err_pathconf,
5189336Sdfr	nfsv3err_commit,
5199336Sdfr};
5209336Sdfr
5211541Srgrimes/*
5221541Srgrimes * Called once to initialize data structures...
5231541Srgrimes */
52483651Speterstatic int
52583700Speternfsrv_modevent(module_t mod, int type, void *data)
5261541Srgrimes{
5271541Srgrimes
52883700Speter	switch (type) {
52983700Speter	case MOD_LOAD:
53083700Speter		rpc_vers = txdr_unsigned(RPC_VER2);
53183700Speter		rpc_call = txdr_unsigned(RPC_CALL);
53283700Speter		rpc_reply = txdr_unsigned(RPC_REPLY);
53383700Speter		rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
53483700Speter		rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
53583700Speter		rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
53683700Speter		rpc_autherr = txdr_unsigned(RPC_AUTHERR);
53783700Speter		rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
53883700Speter		nfs_prog = txdr_unsigned(NFS_PROG);
53983700Speter		nfs_true = txdr_unsigned(TRUE);
54083700Speter		nfs_false = txdr_unsigned(FALSE);
54183700Speter		nfs_xdrneg1 = txdr_unsigned(-1);
54283700Speter		nfs_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
54383700Speter		if (nfs_ticks < 1)
54483700Speter			nfs_ticks = 1;
54583651Speter
54683700Speter		nfsrv_init(0);		/* Init server data structures */
54783700Speter		nfsrv_initcache();	/* Init the server request cache */
5481541Srgrimes
54983700Speter		nfsrv_timer(0);
5501541Srgrimes
55183700Speter		nfs_prev_nfssvc_sy_narg = sysent[SYS_nfssvc].sy_narg;
55283700Speter		sysent[SYS_nfssvc].sy_narg = 2;
55383700Speter		nfs_prev_nfssvc_sy_call = sysent[SYS_nfssvc].sy_call;
55483700Speter		sysent[SYS_nfssvc].sy_call = (sy_call_t *)nfssvc;
55583700Speter		break;
5562997Swollman
55783700Speter		case MOD_UNLOAD:
55883700Speter
55983700Speter		untimeout(nfsrv_timer, (void *)NULL, nfsrv_timer_handle);
56083700Speter		sysent[SYS_nfssvc].sy_narg = nfs_prev_nfssvc_sy_narg;
56183700Speter		sysent[SYS_nfssvc].sy_call = nfs_prev_nfssvc_sy_call;
56283700Speter		break;
56383700Speter	}
56483700Speter	return 0;
5651541Srgrimes}
56683700Speterstatic moduledata_t nfsserver_mod = {
56783700Speter	"nfsserver",
56883700Speter	nfsrv_modevent,
56983700Speter	NULL,
57083700Speter};
57183700SpeterDECLARE_MODULE(nfsserver, nfsserver_mod, SI_SUB_VFS, SI_ORDER_ANY);
5721541Srgrimes
57383700Speter/* So that loader and kldload(2) can find us, wherever we are.. */
57483700SpeterMODULE_VERSION(nfsserver, 1);
57538894Sbde
5761541Srgrimes/*
57727446Sdfr * Set up nameidata for a lookup() call and do it.
57827446Sdfr *
57927446Sdfr * If pubflag is set, this call is done for a lookup operation on the
58027446Sdfr * public filehandle. In that case we allow crossing mountpoints and
58127446Sdfr * absolute pathnames. However, the caller is expected to check that
58227446Sdfr * the lookup result is within the public fs, and deny access if
58327446Sdfr * it is not.
58448125Sjulian *
58548125Sjulian * nfs_namei() clears out garbage fields that namei() might leave garbage.
58648125Sjulian * This is mainly ni_vp and ni_dvp when an error occurs, and ni_dvp when no
58748125Sjulian * error occurs but the parent was not requested.
58848125Sjulian *
58983651Speter * dirp may be set whether an error is returned or not, and must be
59048125Sjulian * released by the caller.
5911541Srgrimes */
5921549Srgrimesint
59383651Speternfs_namei(struct nameidata *ndp, fhandle_t *fhp, int len,
59483651Speter    struct nfssvc_sock *slp, struct sockaddr *nam, struct mbuf **mdp,
59583651Speter    caddr_t *dposp, struct vnode **retdirp, struct thread *td, int pubflag)
5961541Srgrimes{
59783651Speter	int i, rem;
59883651Speter	struct mbuf *md;
59983651Speter	char *fromcp, *tocp, *cp;
60027446Sdfr	struct iovec aiov;
60127446Sdfr	struct uio auio;
6021541Srgrimes	struct vnode *dp;
60327446Sdfr	int error, rdonly, linklen;
6041541Srgrimes	struct componentname *cnp = &ndp->ni_cnd;
6051541Srgrimes
6069336Sdfr	*retdirp = (struct vnode *)0;
60729653Sdyson	cnp->cn_pnbuf = zalloc(namei_zone);
60829653Sdyson
6091541Srgrimes	/*
6101541Srgrimes	 * Copy the name from the mbuf list to ndp->ni_pnbuf
6111541Srgrimes	 * and set the various ndp fields appropriately.
6121541Srgrimes	 */
6131541Srgrimes	fromcp = *dposp;
6141541Srgrimes	tocp = cnp->cn_pnbuf;
6151541Srgrimes	md = *mdp;
6161541Srgrimes	rem = mtod(md, caddr_t) + md->m_len - fromcp;
6171541Srgrimes	for (i = 0; i < len; i++) {
6181541Srgrimes		while (rem == 0) {
6191541Srgrimes			md = md->m_next;
6201541Srgrimes			if (md == NULL) {
6211541Srgrimes				error = EBADRPC;
6221541Srgrimes				goto out;
6231541Srgrimes			}
6241541Srgrimes			fromcp = mtod(md, caddr_t);
6251541Srgrimes			rem = md->m_len;
6261541Srgrimes		}
62727446Sdfr		if (*fromcp == '\0' || (!pubflag && *fromcp == '/')) {
6289336Sdfr			error = EACCES;
6291541Srgrimes			goto out;
6301541Srgrimes		}
6311541Srgrimes		*tocp++ = *fromcp++;
6321541Srgrimes		rem--;
6331541Srgrimes	}
6341541Srgrimes	*tocp = '\0';
6351541Srgrimes	*mdp = md;
6361541Srgrimes	*dposp = fromcp;
6371541Srgrimes	len = nfsm_rndup(len)-len;
6381541Srgrimes	if (len > 0) {
6391541Srgrimes		if (rem >= len)
6401541Srgrimes			*dposp += len;
64127609Sdfr		else if ((error = nfs_adv(mdp, dposp, len, rem)) != 0)
6429336Sdfr			goto out;
6431541Srgrimes	}
64427446Sdfr
6451541Srgrimes	/*
6461541Srgrimes	 * Extract and set starting directory.
6471541Srgrimes	 */
64827446Sdfr	error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
64983651Speter	    nam, &rdonly, pubflag);
65027446Sdfr	if (error)
6511541Srgrimes		goto out;
6521541Srgrimes	if (dp->v_type != VDIR) {
65317761Sdyson		vrele(dp);
6541541Srgrimes		error = ENOTDIR;
6551541Srgrimes		goto out;
6561541Srgrimes	}
65727446Sdfr
65827446Sdfr	if (rdonly)
65927446Sdfr		cnp->cn_flags |= RDONLY;
66027446Sdfr
66148125Sjulian	/*
66283651Speter	 * Set return directory.  Reference to dp is implicitly transfered
66348125Sjulian	 * to the returned pointer
66448125Sjulian	 */
66527609Sdfr	*retdirp = dp;
66627609Sdfr
66727446Sdfr	if (pubflag) {
66827446Sdfr		/*
66927446Sdfr		 * Oh joy. For WebNFS, handle those pesky '%' escapes,
67027446Sdfr		 * and the 'native path' indicator.
67127446Sdfr		 */
67229653Sdyson		cp = zalloc(namei_zone);
67327446Sdfr		fromcp = cnp->cn_pnbuf;
67427446Sdfr		tocp = cp;
67527446Sdfr		if ((unsigned char)*fromcp >= WEBNFS_SPECCHAR_START) {
67627446Sdfr			switch ((unsigned char)*fromcp) {
67727446Sdfr			case WEBNFS_NATIVE_CHAR:
67827446Sdfr				/*
67927446Sdfr				 * 'Native' path for us is the same
68027446Sdfr				 * as a path according to the NFS spec,
68127446Sdfr				 * just skip the escape char.
68227446Sdfr				 */
68327446Sdfr				fromcp++;
68427446Sdfr				break;
68527446Sdfr			/*
68627446Sdfr			 * More may be added in the future, range 0x80-0xff
68727446Sdfr			 */
68827446Sdfr			default:
68927446Sdfr				error = EIO;
69029653Sdyson				zfree(namei_zone, cp);
69127446Sdfr				goto out;
69227446Sdfr			}
69327446Sdfr		}
69427446Sdfr		/*
69527446Sdfr		 * Translate the '%' escapes, URL-style.
69627446Sdfr		 */
69727446Sdfr		while (*fromcp != '\0') {
69827446Sdfr			if (*fromcp == WEBNFS_ESC_CHAR) {
69927446Sdfr				if (fromcp[1] != '\0' && fromcp[2] != '\0') {
70027446Sdfr					fromcp++;
70127446Sdfr					*tocp++ = HEXSTRTOI(fromcp);
70227446Sdfr					fromcp += 2;
70327446Sdfr					continue;
70427446Sdfr				} else {
70527446Sdfr					error = ENOENT;
70629653Sdyson					zfree(namei_zone, cp);
70727446Sdfr					goto out;
70827446Sdfr				}
70927446Sdfr			} else
71027446Sdfr				*tocp++ = *fromcp++;
71127446Sdfr		}
71227446Sdfr		*tocp = '\0';
71329653Sdyson		zfree(namei_zone, cnp->cn_pnbuf);
71427446Sdfr		cnp->cn_pnbuf = cp;
71527446Sdfr	}
71627446Sdfr
71727446Sdfr	ndp->ni_pathlen = (tocp - cnp->cn_pnbuf) + 1;
71827446Sdfr	ndp->ni_segflg = UIO_SYSSPACE;
71927446Sdfr
72027446Sdfr	if (pubflag) {
72127446Sdfr		ndp->ni_rootdir = rootvnode;
72227446Sdfr		ndp->ni_loopcnt = 0;
72327446Sdfr		if (cnp->cn_pnbuf[0] == '/')
72427446Sdfr			dp = rootvnode;
72527446Sdfr	} else {
72627609Sdfr		cnp->cn_flags |= NOCROSSMOUNT;
72727446Sdfr	}
72827446Sdfr
72948125Sjulian	/*
73048125Sjulian	 * Initialize for scan, set ni_startdir and bump ref on dp again
73148125Sjulian	 * becuase lookup() will dereference ni_startdir.
73248125Sjulian	 */
73348125Sjulian
73483366Sjulian	cnp->cn_thread = td;
7359336Sdfr	VREF(dp);
73648125Sjulian	ndp->ni_startdir = dp;
73727446Sdfr
73848125Sjulian	for (;;) {
73948125Sjulian		cnp->cn_nameptr = cnp->cn_pnbuf;
74048125Sjulian		/*
74148125Sjulian		 * Call lookup() to do the real work.  If an error occurs,
74248125Sjulian		 * ndp->ni_vp and ni_dvp are left uninitialized or NULL and
74348125Sjulian		 * we do not have to dereference anything before returning.
74448125Sjulian		 * In either case ni_startdir will be dereferenced and NULLed
74548125Sjulian		 * out.
74648125Sjulian		 */
74748125Sjulian		error = lookup(ndp);
74848125Sjulian		if (error)
74948125Sjulian			break;
75048125Sjulian
75148125Sjulian		/*
75283651Speter		 * Check for encountering a symbolic link.  Trivial
75348125Sjulian		 * termination occurs if no symlink encountered.
75448125Sjulian		 * Note: zfree is safe because error is 0, so we will
75548125Sjulian		 * not zfree it again when we break.
75648125Sjulian		 */
75748125Sjulian		if ((cnp->cn_flags & ISSYMLINK) == 0) {
75848125Sjulian			nfsrv_object_create(ndp->ni_vp);
75948125Sjulian			if (cnp->cn_flags & (SAVENAME | SAVESTART))
76048125Sjulian				cnp->cn_flags |= HASBUF;
76148125Sjulian			else
76248125Sjulian				zfree(namei_zone, cnp->cn_pnbuf);
76348125Sjulian			break;
76427446Sdfr		}
76548125Sjulian
76648125Sjulian		/*
76748125Sjulian		 * Validate symlink
76848125Sjulian		 */
7691541Srgrimes		if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
77083366Sjulian			VOP_UNLOCK(ndp->ni_dvp, 0, td);
77127446Sdfr		if (!pubflag) {
77227446Sdfr			error = EINVAL;
77348125Sjulian			goto badlink2;
77427446Sdfr		}
77527446Sdfr
77627446Sdfr		if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
77727446Sdfr			error = ELOOP;
77848125Sjulian			goto badlink2;
77927446Sdfr		}
78027609Sdfr		if (ndp->ni_pathlen > 1)
78129653Sdyson			cp = zalloc(namei_zone);
7821541Srgrimes		else
78327446Sdfr			cp = cnp->cn_pnbuf;
78427446Sdfr		aiov.iov_base = cp;
78527446Sdfr		aiov.iov_len = MAXPATHLEN;
78627446Sdfr		auio.uio_iov = &aiov;
78727446Sdfr		auio.uio_iovcnt = 1;
78827446Sdfr		auio.uio_offset = 0;
78927446Sdfr		auio.uio_rw = UIO_READ;
79027446Sdfr		auio.uio_segflg = UIO_SYSSPACE;
79183366Sjulian		auio.uio_td = (struct thread *)0;
79227446Sdfr		auio.uio_resid = MAXPATHLEN;
79327446Sdfr		error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred);
79427446Sdfr		if (error) {
79548125Sjulian		badlink1:
79627446Sdfr			if (ndp->ni_pathlen > 1)
79729653Sdyson				zfree(namei_zone, cp);
79848125Sjulian		badlink2:
79948125Sjulian			vrele(ndp->ni_dvp);
80048125Sjulian			vput(ndp->ni_vp);
80127446Sdfr			break;
80227446Sdfr		}
80327446Sdfr		linklen = MAXPATHLEN - auio.uio_resid;
80427446Sdfr		if (linklen == 0) {
80527446Sdfr			error = ENOENT;
80648125Sjulian			goto badlink1;
80727446Sdfr		}
80827446Sdfr		if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
80927446Sdfr			error = ENAMETOOLONG;
81048125Sjulian			goto badlink1;
81127446Sdfr		}
81248125Sjulian
81348125Sjulian		/*
81448125Sjulian		 * Adjust or replace path
81548125Sjulian		 */
81627446Sdfr		if (ndp->ni_pathlen > 1) {
81727446Sdfr			bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
81829653Sdyson			zfree(namei_zone, cnp->cn_pnbuf);
81927446Sdfr			cnp->cn_pnbuf = cp;
82027446Sdfr		} else
82127446Sdfr			cnp->cn_pnbuf[linklen] = '\0';
82227446Sdfr		ndp->ni_pathlen += linklen;
82348125Sjulian
82427446Sdfr		/*
82583651Speter		 * Cleanup refs for next loop and check if root directory
82683651Speter		 * should replace current directory.  Normally ni_dvp
82748125Sjulian		 * becomes the new base directory and is cleaned up when
82848125Sjulian		 * we loop.  Explicitly null pointers after invalidation
82948125Sjulian		 * to clarify operation.
83027446Sdfr		 */
83148125Sjulian		vput(ndp->ni_vp);
83248125Sjulian		ndp->ni_vp = NULL;
83348125Sjulian
83427446Sdfr		if (cnp->cn_pnbuf[0] == '/') {
83548125Sjulian			vrele(ndp->ni_dvp);
83648125Sjulian			ndp->ni_dvp = ndp->ni_rootdir;
83748125Sjulian			VREF(ndp->ni_dvp);
83827446Sdfr		}
83948125Sjulian		ndp->ni_startdir = ndp->ni_dvp;
84048125Sjulian		ndp->ni_dvp = NULL;
8411541Srgrimes	}
84248125Sjulian
84348125Sjulian	/*
84448125Sjulian	 * nfs_namei() guarentees that fields will not contain garbage
84548125Sjulian	 * whether an error occurs or not.  This allows the caller to track
84648125Sjulian	 * cleanup state trivially.
84748125Sjulian	 */
8481541Srgrimesout:
84948125Sjulian	if (error) {
85048125Sjulian		zfree(namei_zone, cnp->cn_pnbuf);
85148125Sjulian		ndp->ni_vp = NULL;
85248125Sjulian		ndp->ni_dvp = NULL;
85348125Sjulian		ndp->ni_startdir = NULL;
85448125Sjulian		cnp->cn_flags &= ~HASBUF;
85548125Sjulian	} else if ((ndp->ni_cnd.cn_flags & (WANTPARENT|LOCKPARENT)) == 0) {
85648125Sjulian		ndp->ni_dvp = NULL;
85748125Sjulian	}
8581541Srgrimes	return (error);
8591541Srgrimes}
8601541Srgrimes
8611541Srgrimes/*
8621541Srgrimes * A fiddled version of m_adj() that ensures null fill to a long
8631541Srgrimes * boundary and only trims off the back end
8641541Srgrimes */
8651541Srgrimesvoid
86683651Speternfsm_adj(struct mbuf *mp, int len, int nul)
8671541Srgrimes{
86883651Speter	struct mbuf *m;
86983651Speter	int count, i;
87083651Speter	char *cp;
8711541Srgrimes
8721541Srgrimes	/*
8731541Srgrimes	 * Trim from tail.  Scan the mbuf chain,
8741541Srgrimes	 * calculating its length and finding the last mbuf.
8751541Srgrimes	 * If the adjustment only affects this mbuf, then just
8761541Srgrimes	 * adjust and return.  Otherwise, rescan and truncate
8771541Srgrimes	 * after the remaining size.
8781541Srgrimes	 */
8791541Srgrimes	count = 0;
8801541Srgrimes	m = mp;
8811541Srgrimes	for (;;) {
8821541Srgrimes		count += m->m_len;
8831541Srgrimes		if (m->m_next == (struct mbuf *)0)
8841541Srgrimes			break;
8851541Srgrimes		m = m->m_next;
8861541Srgrimes	}
8871541Srgrimes	if (m->m_len > len) {
8881541Srgrimes		m->m_len -= len;
8891541Srgrimes		if (nul > 0) {
8901541Srgrimes			cp = mtod(m, caddr_t)+m->m_len-nul;
8911541Srgrimes			for (i = 0; i < nul; i++)
8921541Srgrimes				*cp++ = '\0';
8931541Srgrimes		}
8941541Srgrimes		return;
8951541Srgrimes	}
8961541Srgrimes	count -= len;
8971541Srgrimes	if (count < 0)
8981541Srgrimes		count = 0;
8991541Srgrimes	/*
9001541Srgrimes	 * Correct length for chain is "count".
9011541Srgrimes	 * Find the mbuf with last data, adjust its length,
9021541Srgrimes	 * and toss data from remaining mbufs on chain.
9031541Srgrimes	 */
9041541Srgrimes	for (m = mp; m; m = m->m_next) {
9051541Srgrimes		if (m->m_len >= count) {
9061541Srgrimes			m->m_len = count;
9071541Srgrimes			if (nul > 0) {
9081541Srgrimes				cp = mtod(m, caddr_t)+m->m_len-nul;
9091541Srgrimes				for (i = 0; i < nul; i++)
9101541Srgrimes					*cp++ = '\0';
9111541Srgrimes			}
9121541Srgrimes			break;
9131541Srgrimes		}
9141541Srgrimes		count -= m->m_len;
9151541Srgrimes	}
9163305Sphk	for (m = m->m_next;m;m = m->m_next)
9171541Srgrimes		m->m_len = 0;
9181541Srgrimes}
9191541Srgrimes
9201541Srgrimes/*
9219336Sdfr * Make these functions instead of macros, so that the kernel text size
9229336Sdfr * doesn't get too big...
9239336Sdfr */
9249336Sdfrvoid
92583651Speternfsm_srvwcc(struct nfsrv_descript *nfsd, int before_ret,
92683651Speter    struct vattr *before_vap, int after_ret, struct vattr *after_vap,
92783651Speter    struct mbuf **mbp, char **bposp)
9289336Sdfr{
92983651Speter	struct mbuf *mb = *mbp;
93083651Speter	char *bpos = *bposp;
93183651Speter	u_int32_t *tl;
9329336Sdfr
9339336Sdfr	if (before_ret) {
93484002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
9359336Sdfr		*tl = nfs_false;
9369336Sdfr	} else {
93784002Speter		tl = nfsm_build(u_int32_t *, 7 * NFSX_UNSIGNED);
9389336Sdfr		*tl++ = nfs_true;
93947751Speter		txdr_hyper(before_vap->va_size, tl);
9409336Sdfr		tl += 2;
9419336Sdfr		txdr_nfsv3time(&(before_vap->va_mtime), tl);
9429336Sdfr		tl += 2;
9439336Sdfr		txdr_nfsv3time(&(before_vap->va_ctime), tl);
9449336Sdfr	}
9459336Sdfr	*bposp = bpos;
9469336Sdfr	*mbp = mb;
9479336Sdfr	nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp);
9489336Sdfr}
9499336Sdfr
9509336Sdfrvoid
95183651Speternfsm_srvpostopattr(struct nfsrv_descript *nfsd, int after_ret,
95283651Speter    struct vattr *after_vap, struct mbuf **mbp, char **bposp)
9539336Sdfr{
95483651Speter	struct mbuf *mb = *mbp;
95583651Speter	char *bpos = *bposp;
95683651Speter	u_int32_t *tl;
95783651Speter	struct nfs_fattr *fp;
9589336Sdfr
9599336Sdfr	if (after_ret) {
96084002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED);
9619336Sdfr		*tl = nfs_false;
9629336Sdfr	} else {
96384002Speter		tl = nfsm_build(u_int32_t *, NFSX_UNSIGNED + NFSX_V3FATTR);
9649336Sdfr		*tl++ = nfs_true;
9659336Sdfr		fp = (struct nfs_fattr *)tl;
9669336Sdfr		nfsm_srvfattr(nfsd, after_vap, fp);
9679336Sdfr	}
9689336Sdfr	*mbp = mb;
9699336Sdfr	*bposp = bpos;
9709336Sdfr}
9719336Sdfr
9729336Sdfrvoid
97383651Speternfsm_srvfattr(struct nfsrv_descript *nfsd, struct vattr *vap,
97483651Speter    struct nfs_fattr *fp)
9759336Sdfr{
9769336Sdfr
9779336Sdfr	fp->fa_nlink = txdr_unsigned(vap->va_nlink);
9789336Sdfr	fp->fa_uid = txdr_unsigned(vap->va_uid);
9799336Sdfr	fp->fa_gid = txdr_unsigned(vap->va_gid);
9809336Sdfr	if (nfsd->nd_flag & ND_NFSV3) {
9819336Sdfr		fp->fa_type = vtonfsv3_type(vap->va_type);
9829336Sdfr		fp->fa_mode = vtonfsv3_mode(vap->va_mode);
98347751Speter		txdr_hyper(vap->va_size, &fp->fa3_size);
98447751Speter		txdr_hyper(vap->va_bytes, &fp->fa3_used);
98547028Sphk		fp->fa3_rdev.specdata1 = txdr_unsigned(umajor(vap->va_rdev));
98647028Sphk		fp->fa3_rdev.specdata2 = txdr_unsigned(uminor(vap->va_rdev));
9879336Sdfr		fp->fa3_fsid.nfsuquad[0] = 0;
9889336Sdfr		fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid);
9899336Sdfr		fp->fa3_fileid.nfsuquad[0] = 0;
9909336Sdfr		fp->fa3_fileid.nfsuquad[1] = txdr_unsigned(vap->va_fileid);
9919336Sdfr		txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime);
9929336Sdfr		txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime);
9939336Sdfr		txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime);
9949336Sdfr	} else {
9959336Sdfr		fp->fa_type = vtonfsv2_type(vap->va_type);
9969336Sdfr		fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode);
9979336Sdfr		fp->fa2_size = txdr_unsigned(vap->va_size);
9989336Sdfr		fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize);
9999336Sdfr		if (vap->va_type == VFIFO)
10009336Sdfr			fp->fa2_rdev = 0xffffffff;
10019336Sdfr		else
10029336Sdfr			fp->fa2_rdev = txdr_unsigned(vap->va_rdev);
10039336Sdfr		fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE);
10049336Sdfr		fp->fa2_fsid = txdr_unsigned(vap->va_fsid);
10059336Sdfr		fp->fa2_fileid = txdr_unsigned(vap->va_fileid);
10069336Sdfr		txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime);
10079336Sdfr		txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime);
10089336Sdfr		txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime);
10099336Sdfr	}
10109336Sdfr}
10119336Sdfr
10129336Sdfr/*
10131541Srgrimes * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
10141541Srgrimes * 	- look up fsid in mount list (if not found ret error)
10151541Srgrimes *	- get vp and export rights by calling VFS_FHTOVP()
10161541Srgrimes *	- if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
10171541Srgrimes *	- if not lockflag unlock it with VOP_UNLOCK()
10181541Srgrimes */
10191549Srgrimesint
102083651Speternfsrv_fhtovp(fhandle_t *fhp, int lockflag, struct vnode **vpp,
102183651Speter    struct ucred *cred, struct nfssvc_sock *slp, struct sockaddr *nam,
102283651Speter    int *rdonlyp, int pubflag)
10231541Srgrimes{
102483366Sjulian	struct thread *td = curthread; /* XXX */
102583651Speter	struct mount *mp;
102683651Speter	int i;
10271541Srgrimes	struct ucred *credanon;
10281541Srgrimes	int error, exflags;
102936534Speter#ifdef MNT_EXNORESPORT		/* XXX needs mountd and /etc/exports help yet */
103036534Speter	struct sockaddr_int *saddr;
103136534Speter#endif
10321541Srgrimes
10331541Srgrimes	*vpp = (struct vnode *)0;
103427446Sdfr
103527446Sdfr	if (nfs_ispublicfh(fhp)) {
103627446Sdfr		if (!pubflag || !nfs_pub.np_valid)
103727446Sdfr			return (ESTALE);
103827446Sdfr		fhp = &nfs_pub.np_handle;
103927446Sdfr	}
104027446Sdfr
104122521Sdyson	mp = vfs_getvfs(&fhp->fh_fsid);
10423305Sphk	if (!mp)
10431541Srgrimes		return (ESTALE);
104451138Salfred	error = VFS_CHECKEXP(mp, nam, &exflags, &credanon);
10453305Sphk	if (error)
104683651Speter		return (error);
104751138Salfred	error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp);
104851138Salfred	if (error)
10491541Srgrimes		return (error);
105036534Speter#ifdef MNT_EXNORESPORT
105136534Speter	if (!(exflags & (MNT_EXNORESPORT|MNT_EXPUBLIC))) {
105236534Speter		saddr = (struct sockaddr_in *)nam;
105336534Speter		if (saddr->sin_family == AF_INET &&
105436534Speter		    ntohs(saddr->sin_port) >= IPPORT_RESERVED) {
105536534Speter			vput(*vpp);
105654485Sdillon			*vpp = NULL;
105736534Speter			return (NFSERR_AUTHERR | AUTH_TOOWEAK);
105836534Speter		}
105936534Speter	}
106036534Speter#endif
10611541Srgrimes	/*
10621541Srgrimes	 * Check/setup credentials.
10631541Srgrimes	 */
106483651Speter	if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
10651541Srgrimes		cred->cr_uid = credanon->cr_uid;
10661541Srgrimes		for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
10671541Srgrimes			cred->cr_groups[i] = credanon->cr_groups[i];
10683664Sphk		cred->cr_ngroups = i;
10691541Srgrimes	}
10701541Srgrimes	if (exflags & MNT_EXRDONLY)
10711541Srgrimes		*rdonlyp = 1;
10721541Srgrimes	else
10731541Srgrimes		*rdonlyp = 0;
10747969Sdyson
107517761Sdyson	nfsrv_object_create(*vpp);
10767969Sdyson
10771541Srgrimes	if (!lockflag)
107883366Sjulian		VOP_UNLOCK(*vpp, 0, td);
10791541Srgrimes	return (0);
10801541Srgrimes}
10811541Srgrimes
108227446Sdfr
108327446Sdfr/*
108427446Sdfr * WebNFS: check if a filehandle is a public filehandle. For v3, this
108527446Sdfr * means a length of 0, for v2 it means all zeroes. nfsm_srvmtofh has
108627446Sdfr * transformed this to all zeroes in both cases, so check for it.
108727446Sdfr */
108827446Sdfrint
108983651Speternfs_ispublicfh(fhandle_t *fhp)
109027446Sdfr{
109127446Sdfr	char *cp = (char *)fhp;
109227446Sdfr	int i;
109327446Sdfr
109427446Sdfr	for (i = 0; i < NFSX_V3FH; i++)
109527446Sdfr		if (*cp++ != 0)
109627446Sdfr			return (FALSE);
109727446Sdfr	return (TRUE);
109827446Sdfr}
109983651Speter
11001541Srgrimes/*
11011541Srgrimes * This function compares two net addresses by family and returns TRUE
11021541Srgrimes * if they are the same host.
11031541Srgrimes * If there is any doubt, return FALSE.
11041541Srgrimes * The AF_INET family is handled as a special case so that address mbufs
11051541Srgrimes * don't need to be saved to store "struct in_addr", which is only 4 bytes.
11061541Srgrimes */
11071549Srgrimesint
110883651Speternetaddr_match(int family, union nethostaddr *haddr, struct sockaddr *nam)
11091541Srgrimes{
111083651Speter	struct sockaddr_in *inetaddr;
11111541Srgrimes
11121541Srgrimes	switch (family) {
11131541Srgrimes	case AF_INET:
111428270Swollman		inetaddr = (struct sockaddr_in *)nam;
11151541Srgrimes		if (inetaddr->sin_family == AF_INET &&
11161541Srgrimes		    inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
11171541Srgrimes			return (1);
11181541Srgrimes		break;
11191541Srgrimes	default:
11201541Srgrimes		break;
11211541Srgrimes	};
11221541Srgrimes	return (0);
11231541Srgrimes}
11245455Sdg
11259336Sdfr/*
11269336Sdfr * Map errnos to NFS error numbers. For Version 3 also filter out error
11279336Sdfr * numbers not specified for the associated procedure.
11289336Sdfr */
11295455Sdgint
113083651Speternfsrv_errmap(struct nfsrv_descript *nd, int err)
11319336Sdfr{
113283651Speter	short *defaulterrp, *errp;
11339336Sdfr
11349336Sdfr	if (nd->nd_flag & ND_NFSV3) {
11359336Sdfr	    if (nd->nd_procnum <= NFSPROC_COMMIT) {
11369336Sdfr		errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum];
11379336Sdfr		while (*++errp) {
11389336Sdfr			if (*errp == err)
11399336Sdfr				return (err);
11409336Sdfr			else if (*errp > err)
11419336Sdfr				break;
11429336Sdfr		}
11439336Sdfr		return ((int)*defaulterrp);
11449336Sdfr	    } else
11459336Sdfr		return (err & 0xffff);
11469336Sdfr	}
11479336Sdfr	if (err <= ELAST)
11489336Sdfr		return ((int)nfsrv_v2errmap[err - 1]);
11499336Sdfr	return (NFSERR_IO);
11509336Sdfr}
11519336Sdfr
11529336Sdfrint
115383651Speternfsrv_object_create(struct vnode *vp)
115431886Sbde{
11555455Sdg
115631886Sbde	if (vp == NULL || vp->v_type != VREG)
115731886Sbde		return (1);
115883366Sjulian	return (vfs_object_create(vp, curthread,
115983366Sjulian			  curthread ? curthread->td_proc->p_ucred : NULL));
11605455Sdg}
116136503Speter
116236503Speter/*
116336503Speter * Sort the group list in increasing numerical order.
116436503Speter * (Insertion sort by Chris Torek, who was grossed out by the bubble sort
116536503Speter *  that used to be here.)
116636503Speter */
116736503Spetervoid
116883651Speternfsrvw_sort(gid_t *list, int num)
116936503Speter{
117083651Speter	int i, j;
117136503Speter	gid_t v;
117236503Speter
117336503Speter	/* Insertion sort. */
117436503Speter	for (i = 1; i < num; i++) {
117536503Speter		v = list[i];
117636503Speter		/* find correct slot for value v, moving others up */
117736503Speter		for (j = i; --j >= 0 && v < list[j];)
117836503Speter			list[j + 1] = list[j];
117936503Speter		list[j + 1] = v;
118036503Speter	}
118136503Speter}
118236503Speter
118336503Speter/*
118436503Speter * copy credentials making sure that the result can be compared with bcmp().
118536503Speter */
118636503Spetervoid
118783651Speternfsrv_setcred(struct ucred *incred, struct ucred *outcred)
118836503Speter{
118983651Speter	int i;
119036503Speter
119136503Speter	bzero((caddr_t)outcred, sizeof (struct ucred));
119236503Speter	outcred->cr_ref = 1;
119336503Speter	outcred->cr_uid = incred->cr_uid;
119436503Speter	outcred->cr_ngroups = incred->cr_ngroups;
119536503Speter	for (i = 0; i < incred->cr_ngroups; i++)
119636503Speter		outcred->cr_groups[i] = incred->cr_groups[i];
119736503Speter	nfsrvw_sort(outcred->cr_groups, outcred->cr_ngroups);
119836503Speter}
119983651Speter
120083651Speter/*
120183651Speter * Helper functions for macros.
120283651Speter */
120383651Speter
120483651Spetervoid
120583651Speternfsm_srvfhtom_xx(fhandle_t *f, int v3,
120683651Speter    u_int32_t **tl, struct mbuf **mb, caddr_t *bpos)
120783651Speter{
120883651Speter	u_int32_t *cp;
120983651Speter
121083651Speter	if (v3) {
121184002Speter		*tl = nfsm_build_xx(NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
121283651Speter		*(*tl)++ = txdr_unsigned(NFSX_V3FH);
121383651Speter		bcopy(f, (*tl), NFSX_V3FH);
121483651Speter	} else {
121584002Speter		cp = nfsm_build_xx(NFSX_V2FH, mb, bpos);
121683651Speter		bcopy(f, cp, NFSX_V2FH);
121783651Speter	}
121883651Speter}
121983651Speter
122083651Spetervoid
122183651Speternfsm_srvpostop_fh_xx(fhandle_t *f,
122283651Speter    u_int32_t **tl, struct mbuf **mb, caddr_t *bpos)
122383651Speter{
122484002Speter
122584002Speter	*tl = nfsm_build_xx(2 * NFSX_UNSIGNED + NFSX_V3FH, mb, bpos);
122683651Speter	*(*tl)++ = nfs_true;
122783651Speter	*(*tl)++ = txdr_unsigned(NFSX_V3FH);
122883651Speter	bcopy(f, (*tl), NFSX_V3FH);
122983651Speter}
123083651Speter
123183651Speterint
123283651Speternfsm_srvstrsiz_xx(int *s, int m,
123383651Speter    u_int32_t **tl, struct mbuf **md, caddr_t *dpos)
123483651Speter{
123583651Speter	int ret;
123683651Speter
123783651Speter	ret = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
123883651Speter	if (ret)
123983651Speter		return ret;
124083651Speter	*s = fxdr_unsigned(int32_t, **tl);
124183651Speter	if (*s > m || *s <= 0)
124283651Speter		return EBADRPC;
124383651Speter	return 0;
124483651Speter}
124583651Speter
124683651Speterint
124783651Speternfsm_srvnamesiz_xx(int *s,
124883651Speter    u_int32_t **tl, struct mbuf **md, caddr_t *dpos)
124983651Speter{
125083651Speter	int ret;
125183651Speter
125283651Speter	ret = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
125383651Speter	if (ret)
125483651Speter		return ret;
125583651Speter	*s = fxdr_unsigned(int32_t, **tl);
125683651Speter	if (*s > NFS_MAXNAMLEN)
125783651Speter		return NFSERR_NAMETOL;
125883651Speter	if (*s <= 0)
125983651Speter		return EBADRPC;
126083651Speter	return 0;
126183651Speter}
126283651Speter
126383651Spetervoid
126483651Speternfsm_clget_xx(u_int32_t **tl, struct mbuf *mb, struct mbuf **mp,
126583651Speter    char **bp, char **be, caddr_t bpos)
126683651Speter{
126783651Speter	struct mbuf *nmp;
126883651Speter
126983651Speter	if (*bp >= *be) {
127083651Speter		if (*mp == mb)
127183651Speter			(*mp)->m_len += *bp - bpos;
127283651Speter		MGET(nmp, M_TRYWAIT, MT_DATA);
127383651Speter		MCLGET(nmp, M_TRYWAIT);
127483651Speter		nmp->m_len = NFSMSIZ(nmp);
127583651Speter		(*mp)->m_next = nmp;
127683651Speter		*mp = nmp;
127783651Speter		*bp = mtod(*mp, caddr_t);
127883651Speter		*be = *bp + (*mp)->m_len;
127983651Speter	}
128083651Speter	*tl = (u_int32_t *)*bp;
128183651Speter}
128283651Speter
128383651Speterint
128483651Speternfsm_srvmtofh_xx(fhandle_t *f, struct nfsrv_descript *nfsd,
128583651Speter    u_int32_t **tl, struct mbuf **md, caddr_t *dpos)
128683651Speter{
128783651Speter	int error;
128883651Speter	int fhlen;
128983651Speter
129083651Speter	if (nfsd->nd_flag & ND_NFSV3) {
129183651Speter		error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
129283651Speter		if (error)
129383651Speter			return error;
129483651Speter		fhlen = fxdr_unsigned(int, **tl);
129583651Speter		if (fhlen != 0 && fhlen != NFSX_V3FH)
129683651Speter			return EBADRPC;
129783651Speter	} else {
129883651Speter		fhlen = NFSX_V2FH;
129983651Speter	}
130083651Speter	if (fhlen != 0) {
130183651Speter		error = nfsm_dissect_xx((void **)tl, fhlen, md, dpos);
130283651Speter		if (error)
130383651Speter			return error;
130483651Speter		bcopy((caddr_t)*tl, (caddr_t)(f), fhlen);
130583651Speter	} else {
130683651Speter		bzero((caddr_t)(f), NFSX_V3FH);
130783651Speter	}
130883651Speter	return 0;
130983651Speter}
131083651Speter
131183651Speterint
131283651Speternfsm_srvsattr_xx(struct vattr *a,
131383651Speter    u_int32_t **tl, struct mbuf **md, caddr_t *dpos)
131483651Speter{
131583651Speter	int error = 0;
131683651Speter
131783651Speter	error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
131883651Speter	if (error)
131983651Speter		goto bugout;
132083651Speter	if (**tl == nfs_true) {
132183651Speter		error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
132283651Speter		if (error)
132383651Speter			goto bugout;
132483651Speter		(a)->va_mode = nfstov_mode(**tl);
132583651Speter	}
132683651Speter	error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
132783651Speter	if (error)
132883651Speter		goto bugout;
132983651Speter	if (**tl == nfs_true) {
133083651Speter		error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
133183651Speter		if (error)
133283651Speter			goto bugout;
133383651Speter		(a)->va_uid = fxdr_unsigned(uid_t, **tl);
133483651Speter	}
133583651Speter	error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
133683651Speter	if (error)
133783651Speter		goto bugout;
133883651Speter	if (**tl == nfs_true) {
133983651Speter		error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
134083651Speter		if (error)
134183651Speter			goto bugout;
134283651Speter		(a)->va_gid = fxdr_unsigned(gid_t, **tl);
134383651Speter	}
134483651Speter	error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
134583651Speter	if (error)
134683651Speter		goto bugout;
134783651Speter	if (**tl == nfs_true) {
134883651Speter		error = nfsm_dissect_xx((void **)tl, 2 * NFSX_UNSIGNED,
134983651Speter		    md, dpos);
135083651Speter		if (error)
135183651Speter			goto bugout;
135283651Speter		(a)->va_size = fxdr_hyper(*tl);
135383651Speter	}
135483651Speter	error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
135583651Speter	if (error)
135683651Speter		goto bugout;
135783651Speter	switch (fxdr_unsigned(int, **tl)) {
135883651Speter	case NFSV3SATTRTIME_TOCLIENT:
135983651Speter		error = nfsm_dissect_xx((void **)tl, 2 * NFSX_UNSIGNED,
136083651Speter		    md, dpos);
136183651Speter		if (error)
136283651Speter			goto bugout;
136383651Speter		fxdr_nfsv3time(*tl, &(a)->va_atime);
136483651Speter		break;
136583651Speter	case NFSV3SATTRTIME_TOSERVER:
136683651Speter		getnanotime(&(a)->va_atime);
136783651Speter		break;
136883651Speter	}
136983651Speter	error = nfsm_dissect_xx((void **)tl, NFSX_UNSIGNED, md, dpos);
137083651Speter	if (error)
137183651Speter		goto bugout;
137283651Speter	switch (fxdr_unsigned(int, **tl)) {
137383651Speter	case NFSV3SATTRTIME_TOCLIENT:
137483651Speter		error = nfsm_dissect_xx((void **)tl, 2 * NFSX_UNSIGNED,
137583651Speter		    md, dpos);
137683651Speter		if (error)
137783651Speter			goto bugout;
137883651Speter		fxdr_nfsv3time(*tl, &(a)->va_mtime);
137983651Speter		break;
138083651Speter	case NFSV3SATTRTIME_TOSERVER:
138183651Speter		getnanotime(&(a)->va_mtime);
138283651Speter		break;
138383651Speter	}
138483651Speter	return 0;
138583651Speter
138683651Speterbugout:
138783651Speter	return error;
138883651Speter}
138983651Speter
139083651Spetervoid
139183651Speternfs_rephead_xx(int s, struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
139283651Speter    int error, struct mbuf **mrq, struct mbuf **mb,
139383651Speter    struct mbuf **mreq, struct mbuf **mrep, caddr_t *bpos)
139483651Speter{
139583651Speter
139683651Speter	nfs_rephead(s, nfsd, slp, error, mrq, mb, bpos);
139783651Speter	if (*mrep != NULL) {
139883651Speter		m_freem(*mrep);
139983651Speter		*mrep = NULL;
140083651Speter	}
140183651Speter	*mreq = *mrq;
140283651Speter}
1403