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