1191783Srmacklem/*- 2191783Srmacklem * Copyright (c) 1989, 1993, 1995 3191783Srmacklem * The Regents of the University of California. All rights reserved. 4191783Srmacklem * 5191783Srmacklem * This code is derived from software contributed to Berkeley by 6191783Srmacklem * Rick Macklem at The University of Guelph. 7191783Srmacklem * 8191783Srmacklem * Redistribution and use in source and binary forms, with or without 9191783Srmacklem * modification, are permitted provided that the following conditions 10191783Srmacklem * are met: 11191783Srmacklem * 1. Redistributions of source code must retain the above copyright 12191783Srmacklem * notice, this list of conditions and the following disclaimer. 13191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright 14191783Srmacklem * notice, this list of conditions and the following disclaimer in the 15191783Srmacklem * documentation and/or other materials provided with the distribution. 16191783Srmacklem * 4. Neither the name of the University nor the names of its contributors 17191783Srmacklem * may be used to endorse or promote products derived from this software 18191783Srmacklem * without specific prior written permission. 19191783Srmacklem * 20191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23191783Srmacklem * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30191783Srmacklem * SUCH DAMAGE. 31191783Srmacklem * 32191783Srmacklem * from nfs_vfsops.c 8.12 (Berkeley) 5/20/95 33191783Srmacklem */ 34191783Srmacklem 35191783Srmacklem#include <sys/cdefs.h> 36191783Srmacklem__FBSDID("$FreeBSD: releng/11.0/sys/fs/nfsclient/nfs_clvfsops.c 301566 2016-06-07 20:16:01Z cem $"); 37191783Srmacklem 38191783Srmacklem 39191783Srmacklem#include "opt_bootp.h" 40191783Srmacklem#include "opt_nfsroot.h" 41191783Srmacklem 42191783Srmacklem#include <sys/param.h> 43191783Srmacklem#include <sys/systm.h> 44191783Srmacklem#include <sys/kernel.h> 45191783Srmacklem#include <sys/bio.h> 46191783Srmacklem#include <sys/buf.h> 47191783Srmacklem#include <sys/clock.h> 48193066Sjamie#include <sys/jail.h> 49220739Srmacklem#include <sys/limits.h> 50191783Srmacklem#include <sys/lock.h> 51191783Srmacklem#include <sys/malloc.h> 52191783Srmacklem#include <sys/mbuf.h> 53191783Srmacklem#include <sys/module.h> 54191783Srmacklem#include <sys/mount.h> 55191783Srmacklem#include <sys/proc.h> 56191783Srmacklem#include <sys/socket.h> 57191783Srmacklem#include <sys/socketvar.h> 58191783Srmacklem#include <sys/sockio.h> 59191783Srmacklem#include <sys/sysctl.h> 60191783Srmacklem#include <sys/vnode.h> 61191783Srmacklem#include <sys/signalvar.h> 62191783Srmacklem 63191783Srmacklem#include <vm/vm.h> 64191783Srmacklem#include <vm/vm_extern.h> 65191783Srmacklem#include <vm/uma.h> 66191783Srmacklem 67191783Srmacklem#include <net/if.h> 68191783Srmacklem#include <net/route.h> 69191783Srmacklem#include <netinet/in.h> 70191783Srmacklem 71191783Srmacklem#include <fs/nfs/nfsport.h> 72191783Srmacklem#include <fs/nfsclient/nfsnode.h> 73191783Srmacklem#include <fs/nfsclient/nfsmount.h> 74191783Srmacklem#include <fs/nfsclient/nfs.h> 75221040Srmacklem#include <nfs/nfsdiskless.h> 76191783Srmacklem 77219028SnetchildFEATURE(nfscl, "NFSv4 client"); 78219028Snetchild 79191783Srmacklemextern int nfscl_ticks; 80191783Srmacklemextern struct timeval nfsboottime; 81191783Srmacklemextern struct nfsstats newnfsstats; 82222233Srmacklemextern int nfsrv_useacl; 83244042Srmacklemextern int nfscl_debuglevel; 84249630Srmacklemextern enum nfsiod_state ncl_iodwant[NFS_MAXASYNCDAEMON]; 85249630Srmacklemextern struct nfsmount *ncl_iodmount[NFS_MAXASYNCDAEMON]; 86249630Srmacklemextern struct mtx ncl_iod_mutex; 87244042SrmacklemNFSCLSTATEMUTEX; 88191783Srmacklem 89281725StraszMALLOC_DEFINE(M_NEWNFSREQ, "newnfsclient_req", "NFS request header"); 90281725StraszMALLOC_DEFINE(M_NEWNFSMNT, "newnfsmnt", "NFS mount struct"); 91191783Srmacklem 92221973SrmacklemSYSCTL_DECL(_vfs_nfs); 93191783Srmacklemstatic int nfs_ip_paranoia = 1; 94221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, nfs_ip_paranoia, CTLFLAG_RW, 95191783Srmacklem &nfs_ip_paranoia, 0, ""); 96191783Srmacklemstatic int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY; 97221973SrmacklemSYSCTL_INT(_vfs_nfs, NFS_TPRINTF_INITIAL_DELAY, 98191783Srmacklem downdelayinitial, CTLFLAG_RW, &nfs_tprintf_initial_delay, 0, ""); 99191783Srmacklem/* how long between console messages "nfs server foo not responding" */ 100191783Srmacklemstatic int nfs_tprintf_delay = NFS_TPRINTF_DELAY; 101221973SrmacklemSYSCTL_INT(_vfs_nfs, NFS_TPRINTF_DELAY, 102191783Srmacklem downdelayinterval, CTLFLAG_RW, &nfs_tprintf_delay, 0, ""); 103276140Srmacklem#ifdef NFS_DEBUG 104276140Srmacklemint nfs_debug; 105276140SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, debug, CTLFLAG_RW, &nfs_debug, 0, 106276140Srmacklem "Toggle debug flag"); 107276140Srmacklem#endif 108191783Srmacklem 109221040Srmacklemstatic int nfs_mountroot(struct mount *); 110192585Srmacklemstatic void nfs_sec_name(char *, int *); 111191783Srmacklemstatic void nfs_decode_args(struct mount *mp, struct nfsmount *nmp, 112214048Srmacklem struct nfs_args *argp, const char *, struct ucred *, 113214048Srmacklem struct thread *); 114191783Srmacklemstatic int mountnfs(struct nfs_args *, struct mount *, 115221014Srmacklem struct sockaddr *, char *, u_char *, int, u_char *, int, 116221014Srmacklem u_char *, int, struct vnode **, struct ucred *, 117244042Srmacklem struct thread *, int, int, int); 118214053Srmacklemstatic void nfs_getnlminfo(struct vnode *, uint8_t *, size_t *, 119216931Srmacklem struct sockaddr_storage *, int *, off_t *, 120216931Srmacklem struct timeval *); 121191783Srmacklemstatic vfs_mount_t nfs_mount; 122191783Srmacklemstatic vfs_cmount_t nfs_cmount; 123191783Srmacklemstatic vfs_unmount_t nfs_unmount; 124191783Srmacklemstatic vfs_root_t nfs_root; 125191783Srmacklemstatic vfs_statfs_t nfs_statfs; 126191783Srmacklemstatic vfs_sync_t nfs_sync; 127191783Srmacklemstatic vfs_sysctl_t nfs_sysctl; 128255136Srmacklemstatic vfs_purge_t nfs_purge; 129191783Srmacklem 130191783Srmacklem/* 131191783Srmacklem * nfs vfs operations. 132191783Srmacklem */ 133191783Srmacklemstatic struct vfsops nfs_vfsops = { 134191783Srmacklem .vfs_init = ncl_init, 135191783Srmacklem .vfs_mount = nfs_mount, 136191783Srmacklem .vfs_cmount = nfs_cmount, 137191783Srmacklem .vfs_root = nfs_root, 138191783Srmacklem .vfs_statfs = nfs_statfs, 139191783Srmacklem .vfs_sync = nfs_sync, 140191783Srmacklem .vfs_uninit = ncl_uninit, 141191783Srmacklem .vfs_unmount = nfs_unmount, 142191783Srmacklem .vfs_sysctl = nfs_sysctl, 143255136Srmacklem .vfs_purge = nfs_purge, 144191783Srmacklem}; 145247116SjhbVFS_SET(nfs_vfsops, nfs, VFCF_NETWORK | VFCF_SBDRY); 146191783Srmacklem 147191783Srmacklem/* So that loader and kldload(2) can find us, wherever we are.. */ 148221139SrmacklemMODULE_VERSION(nfs, 1); 149221139SrmacklemMODULE_DEPEND(nfs, nfscommon, 1, 1, 1); 150221139SrmacklemMODULE_DEPEND(nfs, krpc, 1, 1, 1); 151221139SrmacklemMODULE_DEPEND(nfs, nfssvc, 1, 1, 1); 152221139SrmacklemMODULE_DEPEND(nfs, nfslock, 1, 1, 1); 153191783Srmacklem 154191783Srmacklem/* 155221066Srmacklem * This structure is now defined in sys/nfs/nfs_diskless.c so that it 156221066Srmacklem * can be shared by both NFS clients. It is declared here so that it 157221066Srmacklem * will be defined for kernels built without NFS_ROOT, although it 158221066Srmacklem * isn't used in that case. 159191783Srmacklem */ 160276096Srmacklem#if !defined(NFS_ROOT) 161221066Srmacklemstruct nfs_diskless nfs_diskless = { { { 0 } } }; 162221066Srmacklemstruct nfsv3_diskless nfsv3_diskless = { { { 0 } } }; 163221066Srmacklemint nfs_diskless_valid = 0; 164221066Srmacklem#endif 165221066Srmacklem 166221973SrmacklemSYSCTL_INT(_vfs_nfs, OID_AUTO, diskless_valid, CTLFLAG_RD, 167221040Srmacklem &nfs_diskless_valid, 0, 168192145Srmacklem "Has the diskless struct been filled correctly"); 169191783Srmacklem 170221973SrmacklemSYSCTL_STRING(_vfs_nfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD, 171221040Srmacklem nfsv3_diskless.root_hostnam, 0, "Path to nfs root"); 172191783Srmacklem 173221973SrmacklemSYSCTL_OPAQUE(_vfs_nfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD, 174221040Srmacklem &nfsv3_diskless.root_saddr, sizeof(nfsv3_diskless.root_saddr), 175192145Srmacklem "%Ssockaddr_in", "Diskless root nfs address"); 176191783Srmacklem 177191783Srmacklem 178191783Srmacklemvoid newnfsargs_ntoh(struct nfs_args *); 179191783Srmacklemstatic int nfs_mountdiskless(char *, 180191783Srmacklem struct sockaddr_in *, struct nfs_args *, 181191783Srmacklem struct thread *, struct vnode **, struct mount *); 182191783Srmacklemstatic void nfs_convert_diskless(void); 183191783Srmacklemstatic void nfs_convert_oargs(struct nfs_args *args, 184191783Srmacklem struct onfs_args *oargs); 185191783Srmacklem 186191783Srmacklemint 187191783Srmacklemnewnfs_iosize(struct nfsmount *nmp) 188191783Srmacklem{ 189191783Srmacklem int iosize, maxio; 190191783Srmacklem 191191783Srmacklem /* First, set the upper limit for iosize */ 192191783Srmacklem if (nmp->nm_flag & NFSMNT_NFSV4) { 193191783Srmacklem maxio = NFS_MAXBSIZE; 194191783Srmacklem } else if (nmp->nm_flag & NFSMNT_NFSV3) { 195191783Srmacklem if (nmp->nm_sotype == SOCK_DGRAM) 196191783Srmacklem maxio = NFS_MAXDGRAMDATA; 197191783Srmacklem else 198191783Srmacklem maxio = NFS_MAXBSIZE; 199191783Srmacklem } else { 200191783Srmacklem maxio = NFS_V2MAXDATA; 201191783Srmacklem } 202191783Srmacklem if (nmp->nm_rsize > maxio || nmp->nm_rsize == 0) 203191783Srmacklem nmp->nm_rsize = maxio; 204281960Srmacklem if (nmp->nm_rsize > NFS_MAXBSIZE) 205281960Srmacklem nmp->nm_rsize = NFS_MAXBSIZE; 206191783Srmacklem if (nmp->nm_readdirsize > maxio || nmp->nm_readdirsize == 0) 207191783Srmacklem nmp->nm_readdirsize = maxio; 208191783Srmacklem if (nmp->nm_readdirsize > nmp->nm_rsize) 209191783Srmacklem nmp->nm_readdirsize = nmp->nm_rsize; 210191783Srmacklem if (nmp->nm_wsize > maxio || nmp->nm_wsize == 0) 211191783Srmacklem nmp->nm_wsize = maxio; 212281960Srmacklem if (nmp->nm_wsize > NFS_MAXBSIZE) 213281960Srmacklem nmp->nm_wsize = NFS_MAXBSIZE; 214191783Srmacklem 215191783Srmacklem /* 216191783Srmacklem * Calculate the size used for io buffers. Use the larger 217191783Srmacklem * of the two sizes to minimise nfs requests but make sure 218191783Srmacklem * that it is at least one VM page to avoid wasting buffer 219290970Srmacklem * space. It must also be at least NFS_DIRBLKSIZ, since 220290970Srmacklem * that is the buffer size used for directories. 221191783Srmacklem */ 222191783Srmacklem iosize = imax(nmp->nm_rsize, nmp->nm_wsize); 223191783Srmacklem iosize = imax(iosize, PAGE_SIZE); 224290970Srmacklem iosize = imax(iosize, NFS_DIRBLKSIZ); 225191783Srmacklem nmp->nm_mountp->mnt_stat.f_iosize = iosize; 226191783Srmacklem return (iosize); 227191783Srmacklem} 228191783Srmacklem 229191783Srmacklemstatic void 230191783Srmacklemnfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs) 231191783Srmacklem{ 232191783Srmacklem 233191783Srmacklem args->version = NFS_ARGSVERSION; 234191783Srmacklem args->addr = oargs->addr; 235191783Srmacklem args->addrlen = oargs->addrlen; 236191783Srmacklem args->sotype = oargs->sotype; 237191783Srmacklem args->proto = oargs->proto; 238191783Srmacklem args->fh = oargs->fh; 239191783Srmacklem args->fhsize = oargs->fhsize; 240191783Srmacklem args->flags = oargs->flags; 241191783Srmacklem args->wsize = oargs->wsize; 242191783Srmacklem args->rsize = oargs->rsize; 243191783Srmacklem args->readdirsize = oargs->readdirsize; 244191783Srmacklem args->timeo = oargs->timeo; 245191783Srmacklem args->retrans = oargs->retrans; 246191783Srmacklem args->readahead = oargs->readahead; 247191783Srmacklem args->hostname = oargs->hostname; 248191783Srmacklem} 249191783Srmacklem 250191783Srmacklemstatic void 251191783Srmacklemnfs_convert_diskless(void) 252191783Srmacklem{ 253191783Srmacklem 254221040Srmacklem bcopy(&nfs_diskless.myif, &nfsv3_diskless.myif, 255221040Srmacklem sizeof(struct ifaliasreq)); 256221040Srmacklem bcopy(&nfs_diskless.mygateway, &nfsv3_diskless.mygateway, 257221040Srmacklem sizeof(struct sockaddr_in)); 258221040Srmacklem nfs_convert_oargs(&nfsv3_diskless.root_args,&nfs_diskless.root_args); 259221040Srmacklem if (nfsv3_diskless.root_args.flags & NFSMNT_NFSV3) { 260221040Srmacklem nfsv3_diskless.root_fhsize = NFSX_MYFH; 261221040Srmacklem bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_MYFH); 262191783Srmacklem } else { 263221040Srmacklem nfsv3_diskless.root_fhsize = NFSX_V2FH; 264221040Srmacklem bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_V2FH); 265191783Srmacklem } 266221040Srmacklem bcopy(&nfs_diskless.root_saddr,&nfsv3_diskless.root_saddr, 267221040Srmacklem sizeof(struct sockaddr_in)); 268221040Srmacklem bcopy(nfs_diskless.root_hostnam, nfsv3_diskless.root_hostnam, MNAMELEN); 269221040Srmacklem nfsv3_diskless.root_time = nfs_diskless.root_time; 270221040Srmacklem bcopy(nfs_diskless.my_hostnam, nfsv3_diskless.my_hostnam, 271221040Srmacklem MAXHOSTNAMELEN); 272221040Srmacklem nfs_diskless_valid = 3; 273191783Srmacklem} 274191783Srmacklem 275191783Srmacklem/* 276191783Srmacklem * nfs statfs call 277191783Srmacklem */ 278191783Srmacklemstatic int 279191990Sattilionfs_statfs(struct mount *mp, struct statfs *sbp) 280191783Srmacklem{ 281191783Srmacklem struct vnode *vp; 282191990Sattilio struct thread *td; 283191783Srmacklem struct nfsmount *nmp = VFSTONFS(mp); 284191783Srmacklem struct nfsvattr nfsva; 285191783Srmacklem struct nfsfsinfo fs; 286191783Srmacklem struct nfsstatfs sb; 287191783Srmacklem int error = 0, attrflag, gotfsinfo = 0, ret; 288191783Srmacklem struct nfsnode *np; 289191783Srmacklem 290191990Sattilio td = curthread; 291191990Sattilio 292191783Srmacklem error = vfs_busy(mp, MBF_NOWAIT); 293191783Srmacklem if (error) 294191783Srmacklem return (error); 295220732Srmacklem error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, LK_EXCLUSIVE); 296191783Srmacklem if (error) { 297191783Srmacklem vfs_unbusy(mp); 298191783Srmacklem return (error); 299191783Srmacklem } 300191783Srmacklem vp = NFSTOV(np); 301191783Srmacklem mtx_lock(&nmp->nm_mtx); 302191783Srmacklem if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) { 303191783Srmacklem mtx_unlock(&nmp->nm_mtx); 304191783Srmacklem error = nfsrpc_fsinfo(vp, &fs, td->td_ucred, td, &nfsva, 305191783Srmacklem &attrflag, NULL); 306191783Srmacklem if (!error) 307191783Srmacklem gotfsinfo = 1; 308191783Srmacklem } else 309191783Srmacklem mtx_unlock(&nmp->nm_mtx); 310191783Srmacklem if (!error) 311191783Srmacklem error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva, 312191783Srmacklem &attrflag, NULL); 313244042Srmacklem if (error != 0) 314244042Srmacklem NFSCL_DEBUG(2, "statfs=%d\n", error); 315191783Srmacklem if (attrflag == 0) { 316191783Srmacklem ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1, 317244042Srmacklem td->td_ucred, td, &nfsva, NULL, NULL); 318191783Srmacklem if (ret) { 319191783Srmacklem /* 320191783Srmacklem * Just set default values to get things going. 321191783Srmacklem */ 322191783Srmacklem NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr)); 323191783Srmacklem nfsva.na_vattr.va_type = VDIR; 324191783Srmacklem nfsva.na_vattr.va_mode = 0777; 325191783Srmacklem nfsva.na_vattr.va_nlink = 100; 326191783Srmacklem nfsva.na_vattr.va_uid = (uid_t)0; 327191783Srmacklem nfsva.na_vattr.va_gid = (gid_t)0; 328191783Srmacklem nfsva.na_vattr.va_fileid = 2; 329191783Srmacklem nfsva.na_vattr.va_gen = 1; 330191783Srmacklem nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE; 331191783Srmacklem nfsva.na_vattr.va_size = 512 * 1024; 332191783Srmacklem } 333191783Srmacklem } 334191783Srmacklem (void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1); 335191783Srmacklem if (!error) { 336191783Srmacklem mtx_lock(&nmp->nm_mtx); 337191783Srmacklem if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4)) 338191783Srmacklem nfscl_loadfsinfo(nmp, &fs); 339191783Srmacklem nfscl_loadsbinfo(nmp, &sb, sbp); 340191783Srmacklem sbp->f_iosize = newnfs_iosize(nmp); 341191783Srmacklem mtx_unlock(&nmp->nm_mtx); 342191783Srmacklem if (sbp != &mp->mnt_stat) { 343191783Srmacklem bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); 344191783Srmacklem bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); 345191783Srmacklem } 346191783Srmacklem strncpy(&sbp->f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN); 347191783Srmacklem } else if (NFS_ISV4(vp)) { 348191783Srmacklem error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); 349191783Srmacklem } 350191783Srmacklem vput(vp); 351191783Srmacklem vfs_unbusy(mp); 352191783Srmacklem return (error); 353191783Srmacklem} 354191783Srmacklem 355191783Srmacklem/* 356191783Srmacklem * nfs version 3 fsinfo rpc call 357191783Srmacklem */ 358191783Srmacklemint 359191783Srmacklemncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred, 360191783Srmacklem struct thread *td) 361191783Srmacklem{ 362191783Srmacklem struct nfsfsinfo fs; 363191783Srmacklem struct nfsvattr nfsva; 364191783Srmacklem int error, attrflag; 365191783Srmacklem 366191783Srmacklem error = nfsrpc_fsinfo(vp, &fs, cred, td, &nfsva, &attrflag, NULL); 367191783Srmacklem if (!error) { 368191783Srmacklem if (attrflag) 369191783Srmacklem (void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 370191783Srmacklem 1); 371191783Srmacklem mtx_lock(&nmp->nm_mtx); 372191783Srmacklem nfscl_loadfsinfo(nmp, &fs); 373191783Srmacklem mtx_unlock(&nmp->nm_mtx); 374191783Srmacklem } 375191783Srmacklem return (error); 376191783Srmacklem} 377191783Srmacklem 378191783Srmacklem/* 379191783Srmacklem * Mount a remote root fs via. nfs. This depends on the info in the 380221040Srmacklem * nfs_diskless structure that has been filled in properly by some primary 381191783Srmacklem * bootstrap. 382191783Srmacklem * It goes something like this: 383191783Srmacklem * - do enough of "ifconfig" by calling ifioctl() so that the system 384191783Srmacklem * can talk to the server 385221040Srmacklem * - If nfs_diskless.mygateway is filled in, use that address as 386191783Srmacklem * a default gateway. 387191783Srmacklem * - build the rootfs mount point and call mountnfs() to do the rest. 388191783Srmacklem * 389191783Srmacklem * It is assumed to be safe to read, modify, and write the nfsv3_diskless 390191783Srmacklem * structure, as well as other global NFS client variables here, as 391192145Srmacklem * nfs_mountroot() will be called once in the boot before any other NFS 392191783Srmacklem * client activity occurs. 393191783Srmacklem */ 394221040Srmacklemstatic int 395221040Srmacklemnfs_mountroot(struct mount *mp) 396191783Srmacklem{ 397192145Srmacklem struct thread *td = curthread; 398221040Srmacklem struct nfsv3_diskless *nd = &nfsv3_diskless; 399191783Srmacklem struct socket *so; 400191783Srmacklem struct vnode *vp; 401191783Srmacklem struct ifreq ir; 402193066Sjamie int error; 403191783Srmacklem u_long l; 404191783Srmacklem char buf[128]; 405191783Srmacklem char *cp; 406191783Srmacklem 407191783Srmacklem#if defined(BOOTP_NFSROOT) && defined(BOOTP) 408192145Srmacklem bootpc_init(); /* use bootp to get nfs_diskless filled in */ 409191783Srmacklem#elif defined(NFS_ROOT) 410191783Srmacklem nfs_setup_diskless(); 411191783Srmacklem#endif 412191783Srmacklem 413221040Srmacklem if (nfs_diskless_valid == 0) 414191783Srmacklem return (-1); 415221040Srmacklem if (nfs_diskless_valid == 1) 416191783Srmacklem nfs_convert_diskless(); 417191783Srmacklem 418191783Srmacklem /* 419191783Srmacklem * XXX splnet, so networks will receive... 420191783Srmacklem */ 421191783Srmacklem splnet(); 422191783Srmacklem 423191783Srmacklem /* 424191783Srmacklem * Do enough of ifconfig(8) so that the critical net interface can 425191783Srmacklem * talk to the server. 426191783Srmacklem */ 427191783Srmacklem error = socreate(nd->myif.ifra_addr.sa_family, &so, nd->root_args.sotype, 0, 428191783Srmacklem td->td_ucred, td); 429191783Srmacklem if (error) 430192145Srmacklem panic("nfs_mountroot: socreate(%04x): %d", 431191783Srmacklem nd->myif.ifra_addr.sa_family, error); 432191783Srmacklem 433191783Srmacklem#if 0 /* XXX Bad idea */ 434191783Srmacklem /* 435191783Srmacklem * We might not have been told the right interface, so we pass 436191783Srmacklem * over the first ten interfaces of the same kind, until we get 437191783Srmacklem * one of them configured. 438191783Srmacklem */ 439191783Srmacklem 440191783Srmacklem for (i = strlen(nd->myif.ifra_name) - 1; 441191783Srmacklem nd->myif.ifra_name[i] >= '0' && 442191783Srmacklem nd->myif.ifra_name[i] <= '9'; 443191783Srmacklem nd->myif.ifra_name[i] ++) { 444191783Srmacklem error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td); 445191783Srmacklem if(!error) 446191783Srmacklem break; 447191783Srmacklem } 448191783Srmacklem#endif 449191783Srmacklem error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td); 450191783Srmacklem if (error) 451192145Srmacklem panic("nfs_mountroot: SIOCAIFADDR: %d", error); 452273174Sdavide if ((cp = kern_getenv("boot.netif.mtu")) != NULL) { 453191783Srmacklem ir.ifr_mtu = strtol(cp, NULL, 10); 454191783Srmacklem bcopy(nd->myif.ifra_name, ir.ifr_name, IFNAMSIZ); 455191783Srmacklem freeenv(cp); 456191783Srmacklem error = ifioctl(so, SIOCSIFMTU, (caddr_t)&ir, td); 457191783Srmacklem if (error) 458192145Srmacklem printf("nfs_mountroot: SIOCSIFMTU: %d", error); 459191783Srmacklem } 460191783Srmacklem soclose(so); 461191783Srmacklem 462191783Srmacklem /* 463191783Srmacklem * If the gateway field is filled in, set it as the default route. 464191783Srmacklem * Note that pxeboot will set a default route of 0 if the route 465191783Srmacklem * is not set by the DHCP server. Check also for a value of 0 466191783Srmacklem * to avoid panicking inappropriately in that situation. 467191783Srmacklem */ 468191783Srmacklem if (nd->mygateway.sin_len != 0 && 469191783Srmacklem nd->mygateway.sin_addr.s_addr != 0) { 470191783Srmacklem struct sockaddr_in mask, sin; 471191783Srmacklem 472191783Srmacklem bzero((caddr_t)&mask, sizeof(mask)); 473191783Srmacklem sin = mask; 474191783Srmacklem sin.sin_family = AF_INET; 475191783Srmacklem sin.sin_len = sizeof(sin); 476192145Srmacklem /* XXX MRT use table 0 for this sort of thing */ 477218757Sbz CURVNET_SET(TD_TO_VNET(td)); 478231852Sbz error = rtrequest_fib(RTM_ADD, (struct sockaddr *)&sin, 479191783Srmacklem (struct sockaddr *)&nd->mygateway, 480191783Srmacklem (struct sockaddr *)&mask, 481231852Sbz RTF_UP | RTF_GATEWAY, NULL, RT_DEFAULT_FIB); 482218757Sbz CURVNET_RESTORE(); 483191783Srmacklem if (error) 484192145Srmacklem panic("nfs_mountroot: RTM_ADD: %d", error); 485191783Srmacklem } 486191783Srmacklem 487191783Srmacklem /* 488191783Srmacklem * Create the rootfs mount point. 489191783Srmacklem */ 490191783Srmacklem nd->root_args.fh = nd->root_fh; 491191783Srmacklem nd->root_args.fhsize = nd->root_fhsize; 492191783Srmacklem l = ntohl(nd->root_saddr.sin_addr.s_addr); 493191783Srmacklem snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld:%s", 494191783Srmacklem (l >> 24) & 0xff, (l >> 16) & 0xff, 495191783Srmacklem (l >> 8) & 0xff, (l >> 0) & 0xff, nd->root_hostnam); 496191783Srmacklem printf("NFS ROOT: %s\n", buf); 497192145Srmacklem nd->root_args.hostname = buf; 498191783Srmacklem if ((error = nfs_mountdiskless(buf, 499191783Srmacklem &nd->root_saddr, &nd->root_args, td, &vp, mp)) != 0) { 500191783Srmacklem return (error); 501191783Srmacklem } 502191783Srmacklem 503191783Srmacklem /* 504191783Srmacklem * This is not really an nfs issue, but it is much easier to 505191783Srmacklem * set hostname here and then let the "/etc/rc.xxx" files 506191783Srmacklem * mount the right /var based upon its preset value. 507191783Srmacklem */ 508193066Sjamie mtx_lock(&prison0.pr_mtx); 509194118Sjamie strlcpy(prison0.pr_hostname, nd->my_hostnam, 510194118Sjamie sizeof(prison0.pr_hostname)); 511193066Sjamie mtx_unlock(&prison0.pr_mtx); 512191783Srmacklem inittodr(ntohl(nd->root_time)); 513191783Srmacklem return (0); 514191783Srmacklem} 515191783Srmacklem 516191783Srmacklem/* 517191783Srmacklem * Internal version of mount system call for diskless setup. 518191783Srmacklem */ 519191783Srmacklemstatic int 520191783Srmacklemnfs_mountdiskless(char *path, 521191783Srmacklem struct sockaddr_in *sin, struct nfs_args *args, struct thread *td, 522191783Srmacklem struct vnode **vpp, struct mount *mp) 523191783Srmacklem{ 524191783Srmacklem struct sockaddr *nam; 525221014Srmacklem int dirlen, error; 526221014Srmacklem char *dirpath; 527191783Srmacklem 528221014Srmacklem /* 529221014Srmacklem * Find the directory path in "path", which also has the server's 530221014Srmacklem * name/ip address in it. 531221014Srmacklem */ 532221014Srmacklem dirpath = strchr(path, ':'); 533221014Srmacklem if (dirpath != NULL) 534221014Srmacklem dirlen = strlen(++dirpath); 535221014Srmacklem else 536221014Srmacklem dirlen = 0; 537191783Srmacklem nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK); 538221014Srmacklem if ((error = mountnfs(args, mp, nam, path, NULL, 0, dirpath, dirlen, 539230547Sjhb NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NAMETIMEO, 540244042Srmacklem NFS_DEFAULT_NEGNAMETIMEO, 0)) != 0) { 541192145Srmacklem printf("nfs_mountroot: mount %s on /: %d\n", path, error); 542191783Srmacklem return (error); 543191783Srmacklem } 544191783Srmacklem return (0); 545191783Srmacklem} 546191783Srmacklem 547191783Srmacklemstatic void 548192585Srmacklemnfs_sec_name(char *sec, int *flagsp) 549192585Srmacklem{ 550192585Srmacklem if (!strcmp(sec, "krb5")) 551192585Srmacklem *flagsp |= NFSMNT_KERB; 552192585Srmacklem else if (!strcmp(sec, "krb5i")) 553192585Srmacklem *flagsp |= (NFSMNT_KERB | NFSMNT_INTEGRITY); 554192585Srmacklem else if (!strcmp(sec, "krb5p")) 555192585Srmacklem *flagsp |= (NFSMNT_KERB | NFSMNT_PRIVACY); 556192585Srmacklem} 557192585Srmacklem 558192585Srmacklemstatic void 559191783Srmacklemnfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp, 560214048Srmacklem const char *hostname, struct ucred *cred, struct thread *td) 561191783Srmacklem{ 562273485Srmacklem int s; 563191783Srmacklem int adjsock; 564214048Srmacklem char *p; 565191783Srmacklem 566191783Srmacklem s = splnet(); 567191783Srmacklem 568191783Srmacklem /* 569191783Srmacklem * Set read-only flag if requested; otherwise, clear it if this is 570191783Srmacklem * an update. If this is not an update, then either the read-only 571191783Srmacklem * flag is already clear, or this is a root mount and it was set 572191783Srmacklem * intentionally at some previous point. 573191783Srmacklem */ 574191783Srmacklem if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) { 575191783Srmacklem MNT_ILOCK(mp); 576191783Srmacklem mp->mnt_flag |= MNT_RDONLY; 577191783Srmacklem MNT_IUNLOCK(mp); 578191783Srmacklem } else if (mp->mnt_flag & MNT_UPDATE) { 579191783Srmacklem MNT_ILOCK(mp); 580191783Srmacklem mp->mnt_flag &= ~MNT_RDONLY; 581191783Srmacklem MNT_IUNLOCK(mp); 582191783Srmacklem } 583191783Srmacklem 584191783Srmacklem /* 585191783Srmacklem * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes 586191783Srmacklem * no sense in that context. Also, set up appropriate retransmit 587191783Srmacklem * and soft timeout behavior. 588191783Srmacklem */ 589191783Srmacklem if (argp->sotype == SOCK_STREAM) { 590191783Srmacklem nmp->nm_flag &= ~NFSMNT_NOCONN; 591191783Srmacklem nmp->nm_timeo = NFS_MAXTIMEO; 592220739Srmacklem if ((argp->flags & NFSMNT_NFSV4) != 0) 593220739Srmacklem nmp->nm_retry = INT_MAX; 594220739Srmacklem else 595220739Srmacklem nmp->nm_retry = NFS_RETRANS_TCP; 596191783Srmacklem } 597191783Srmacklem 598220739Srmacklem /* Also clear RDIRPLUS if NFSv2, it crashes some servers */ 599220739Srmacklem if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) { 600220739Srmacklem argp->flags &= ~NFSMNT_RDIRPLUS; 601191783Srmacklem nmp->nm_flag &= ~NFSMNT_RDIRPLUS; 602220739Srmacklem } 603191783Srmacklem 604220739Srmacklem /* Re-bind if rsrvd port requested and wasn't on one */ 605220739Srmacklem adjsock = !(nmp->nm_flag & NFSMNT_RESVPORT) 606220739Srmacklem && (argp->flags & NFSMNT_RESVPORT); 607191783Srmacklem /* Also re-bind if we're switching to/from a connected UDP socket */ 608220739Srmacklem adjsock |= ((nmp->nm_flag & NFSMNT_NOCONN) != 609191783Srmacklem (argp->flags & NFSMNT_NOCONN)); 610191783Srmacklem 611191783Srmacklem /* Update flags atomically. Don't change the lock bits. */ 612191783Srmacklem nmp->nm_flag = argp->flags | nmp->nm_flag; 613191783Srmacklem splx(s); 614191783Srmacklem 615191783Srmacklem if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) { 616191783Srmacklem nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10; 617191783Srmacklem if (nmp->nm_timeo < NFS_MINTIMEO) 618191783Srmacklem nmp->nm_timeo = NFS_MINTIMEO; 619191783Srmacklem else if (nmp->nm_timeo > NFS_MAXTIMEO) 620191783Srmacklem nmp->nm_timeo = NFS_MAXTIMEO; 621191783Srmacklem } 622191783Srmacklem 623191783Srmacklem if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) { 624191783Srmacklem nmp->nm_retry = argp->retrans; 625191783Srmacklem if (nmp->nm_retry > NFS_MAXREXMIT) 626191783Srmacklem nmp->nm_retry = NFS_MAXREXMIT; 627191783Srmacklem } 628191783Srmacklem 629191783Srmacklem if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) { 630191783Srmacklem nmp->nm_wsize = argp->wsize; 631273486Srmacklem /* 632273486Srmacklem * Clip at the power of 2 below the size. There is an 633273486Srmacklem * issue (not isolated) that causes intermittent page 634273486Srmacklem * faults if this is not done. 635273486Srmacklem */ 636273486Srmacklem if (nmp->nm_wsize > NFS_FABLKSIZE) 637273486Srmacklem nmp->nm_wsize = 1 << (fls(nmp->nm_wsize) - 1); 638273486Srmacklem else 639273485Srmacklem nmp->nm_wsize = NFS_FABLKSIZE; 640191783Srmacklem } 641191783Srmacklem 642191783Srmacklem if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) { 643191783Srmacklem nmp->nm_rsize = argp->rsize; 644273486Srmacklem /* 645273486Srmacklem * Clip at the power of 2 below the size. There is an 646273486Srmacklem * issue (not isolated) that causes intermittent page 647273486Srmacklem * faults if this is not done. 648273486Srmacklem */ 649273486Srmacklem if (nmp->nm_rsize > NFS_FABLKSIZE) 650273486Srmacklem nmp->nm_rsize = 1 << (fls(nmp->nm_rsize) - 1); 651273486Srmacklem else 652273485Srmacklem nmp->nm_rsize = NFS_FABLKSIZE; 653191783Srmacklem } 654191783Srmacklem 655191783Srmacklem if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) { 656191783Srmacklem nmp->nm_readdirsize = argp->readdirsize; 657191783Srmacklem } 658191783Srmacklem 659191783Srmacklem if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0) 660191783Srmacklem nmp->nm_acregmin = argp->acregmin; 661191783Srmacklem else 662191783Srmacklem nmp->nm_acregmin = NFS_MINATTRTIMO; 663191783Srmacklem if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0) 664191783Srmacklem nmp->nm_acregmax = argp->acregmax; 665191783Srmacklem else 666191783Srmacklem nmp->nm_acregmax = NFS_MAXATTRTIMO; 667191783Srmacklem if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0) 668191783Srmacklem nmp->nm_acdirmin = argp->acdirmin; 669191783Srmacklem else 670191783Srmacklem nmp->nm_acdirmin = NFS_MINDIRATTRTIMO; 671191783Srmacklem if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0) 672191783Srmacklem nmp->nm_acdirmax = argp->acdirmax; 673191783Srmacklem else 674191783Srmacklem nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO; 675191783Srmacklem if (nmp->nm_acdirmin > nmp->nm_acdirmax) 676191783Srmacklem nmp->nm_acdirmin = nmp->nm_acdirmax; 677191783Srmacklem if (nmp->nm_acregmin > nmp->nm_acregmax) 678191783Srmacklem nmp->nm_acregmin = nmp->nm_acregmax; 679191783Srmacklem 680191783Srmacklem if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) { 681191783Srmacklem if (argp->readahead <= NFS_MAXRAHEAD) 682191783Srmacklem nmp->nm_readahead = argp->readahead; 683191783Srmacklem else 684191783Srmacklem nmp->nm_readahead = NFS_MAXRAHEAD; 685191783Srmacklem } 686191783Srmacklem if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) { 687191783Srmacklem if (argp->wcommitsize < nmp->nm_wsize) 688191783Srmacklem nmp->nm_wcommitsize = nmp->nm_wsize; 689191783Srmacklem else 690191783Srmacklem nmp->nm_wcommitsize = argp->wcommitsize; 691191783Srmacklem } 692191783Srmacklem 693191783Srmacklem adjsock |= ((nmp->nm_sotype != argp->sotype) || 694191783Srmacklem (nmp->nm_soproto != argp->proto)); 695191783Srmacklem 696191783Srmacklem if (nmp->nm_client != NULL && adjsock) { 697191783Srmacklem int haslock = 0, error = 0; 698191783Srmacklem 699191783Srmacklem if (nmp->nm_sotype == SOCK_STREAM) { 700191783Srmacklem error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock); 701191783Srmacklem if (!error) 702191783Srmacklem haslock = 1; 703191783Srmacklem } 704191783Srmacklem if (!error) { 705191783Srmacklem newnfs_disconnect(&nmp->nm_sockreq); 706191783Srmacklem if (haslock) 707191783Srmacklem newnfs_sndunlock(&nmp->nm_sockreq.nr_lock); 708191783Srmacklem nmp->nm_sotype = argp->sotype; 709191783Srmacklem nmp->nm_soproto = argp->proto; 710191783Srmacklem if (nmp->nm_sotype == SOCK_DGRAM) 711191783Srmacklem while (newnfs_connect(nmp, &nmp->nm_sockreq, 712191783Srmacklem cred, td, 0)) { 713191783Srmacklem printf("newnfs_args: retrying connect\n"); 714276096Srmacklem (void) nfs_catnap(PSOCK, 0, "nfscon"); 715191783Srmacklem } 716191783Srmacklem } 717191783Srmacklem } else { 718191783Srmacklem nmp->nm_sotype = argp->sotype; 719191783Srmacklem nmp->nm_soproto = argp->proto; 720191783Srmacklem } 721214048Srmacklem 722214048Srmacklem if (hostname != NULL) { 723214048Srmacklem strlcpy(nmp->nm_hostname, hostname, 724214048Srmacklem sizeof(nmp->nm_hostname)); 725214048Srmacklem p = strchr(nmp->nm_hostname, ':'); 726214048Srmacklem if (p != NULL) 727214048Srmacklem *p = '\0'; 728214048Srmacklem } 729191783Srmacklem} 730191783Srmacklem 731221190Srmacklemstatic const char *nfs_opts[] = { "from", "nfs_args", 732273849Strasz "noac", "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union", 733191783Srmacklem "noclusterr", "noclusterw", "multilabel", "acls", "force", "update", 734192585Srmacklem "async", "noconn", "nolockd", "conn", "lockd", "intr", "rdirplus", 735192585Srmacklem "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize", 736273849Strasz "retrans", "actimeo", "acregmin", "acregmax", "acdirmin", "acdirmax", 737273849Strasz "resvport", "readahead", "hostname", "timeo", "timeout", "addr", "fh", 738273849Strasz "nfsv3", "sec", "principal", "nfsv4", "gssname", "allgssname", "dirpath", 739273849Strasz "minorversion", "nametimeo", "negnametimeo", "nocto", "noncontigwr", 740273849Strasz "pnfs", "wcommitsize", 741191783Srmacklem NULL }; 742191783Srmacklem 743191783Srmacklem/* 744299848Strasz * Parse the "from" mountarg, passed by the generic mount(8) program 745299848Strasz * or the mountroot code. This is used when rerooting into NFS. 746299848Strasz * 747299848Strasz * Note that the "hostname" is actually a "hostname:/share/path" string. 748299848Strasz */ 749299848Straszstatic int 750299848Strasznfs_mount_parse_from(struct vfsoptlist *opts, char **hostnamep, 751299848Strasz struct sockaddr_in **sinp, char *dirpath, size_t dirpathsize, int *dirlenp) 752299848Strasz{ 753299848Strasz char nam[MNAMELEN + 1]; 754299848Strasz char *delimp, *hostp, *spec; 755299848Strasz int error, have_bracket = 0, offset, rv, speclen; 756299848Strasz struct sockaddr_in *sin; 757299848Strasz size_t len; 758299848Strasz 759299848Strasz error = vfs_getopt(opts, "from", (void **)&spec, &speclen); 760299848Strasz if (error != 0) 761299848Strasz return (error); 762299848Strasz 763299848Strasz /* 764299848Strasz * This part comes from sbin/mount_nfs/mount_nfs.c:getnfsargs(). 765299848Strasz */ 766301566Scem if (*spec == '[' && (delimp = strchr(spec + 1, ']')) != NULL && 767301566Scem *(delimp + 1) == ':') { 768301566Scem hostp = spec + 1; 769301566Scem spec = delimp + 2; 770301566Scem have_bracket = 1; 771301566Scem } else if ((delimp = strrchr(spec, ':')) != NULL) { 772301566Scem hostp = spec; 773301566Scem spec = delimp + 1; 774301566Scem } else if ((delimp = strrchr(spec, '@')) != NULL) { 775301566Scem printf("%s: path@server syntax is deprecated, " 776299848Strasz "use server:path\n", __func__); 777301566Scem hostp = delimp + 1; 778301566Scem } else { 779301566Scem printf("%s: no <host>:<dirpath> nfs-name\n", __func__); 780301566Scem return (EINVAL); 781301566Scem } 782301566Scem *delimp = '\0'; 783299848Strasz 784301566Scem /* 785301566Scem * If there has been a trailing slash at mounttime it seems 786301566Scem * that some mountd implementations fail to remove the mount 787301566Scem * entries from their mountlist while unmounting. 788301566Scem */ 789301566Scem for (speclen = strlen(spec); 790301566Scem speclen > 1 && spec[speclen - 1] == '/'; 791301566Scem speclen--) 792301566Scem spec[speclen - 1] = '\0'; 793301566Scem if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) { 794301566Scem printf("%s: %s:%s: name too long", __func__, hostp, spec); 795301566Scem return (EINVAL); 796301566Scem } 797299848Strasz /* Make both '@' and ':' notations equal */ 798299848Strasz if (*hostp != '\0') { 799299848Strasz len = strlen(hostp); 800299848Strasz offset = 0; 801299848Strasz if (have_bracket) 802299848Strasz nam[offset++] = '['; 803299848Strasz memmove(nam + offset, hostp, len); 804299848Strasz if (have_bracket) 805299848Strasz nam[len + offset++] = ']'; 806299848Strasz nam[len + offset++] = ':'; 807299848Strasz memmove(nam + len + offset, spec, speclen); 808299848Strasz nam[len + speclen + offset] = '\0'; 809301564Scem } else 810301564Scem nam[0] = '\0'; 811299848Strasz 812299848Strasz /* 813299848Strasz * XXX: IPv6 814299848Strasz */ 815299848Strasz sin = malloc(sizeof(*sin), M_SONAME, M_WAITOK); 816299848Strasz rv = inet_pton(AF_INET, hostp, &sin->sin_addr); 817299848Strasz if (rv != 1) { 818299848Strasz printf("%s: cannot parse '%s', inet_pton() returned %d\n", 819299848Strasz __func__, hostp, rv); 820299848Strasz free(sin, M_SONAME); 821299848Strasz return (EINVAL); 822299848Strasz } 823299848Strasz 824299848Strasz sin->sin_len = sizeof(*sin); 825299848Strasz sin->sin_family = AF_INET; 826299848Strasz /* 827299848Strasz * XXX: hardcoded port number. 828299848Strasz */ 829299848Strasz sin->sin_port = htons(2049); 830299848Strasz 831299848Strasz *hostnamep = strdup(nam, M_NEWNFSMNT); 832299848Strasz *sinp = sin; 833299848Strasz strlcpy(dirpath, spec, dirpathsize); 834299848Strasz *dirlenp = strlen(dirpath); 835299848Strasz 836299848Strasz return (0); 837299848Strasz} 838299848Strasz 839299848Strasz/* 840191783Srmacklem * VFS Operations. 841191783Srmacklem * 842191783Srmacklem * mount system call 843191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then 844191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before 845300160Sglebius * doing the getsockaddr() call because getsockaddr() allocates an mbuf and 846191783Srmacklem * an error after that means that I have to release the mbuf. 847191783Srmacklem */ 848191783Srmacklem/* ARGSUSED */ 849191783Srmacklemstatic int 850191990Sattilionfs_mount(struct mount *mp) 851191783Srmacklem{ 852191783Srmacklem struct nfs_args args = { 853191783Srmacklem .version = NFS_ARGSVERSION, 854191783Srmacklem .addr = NULL, 855191783Srmacklem .addrlen = sizeof (struct sockaddr_in), 856191783Srmacklem .sotype = SOCK_STREAM, 857191783Srmacklem .proto = 0, 858191783Srmacklem .fh = NULL, 859191783Srmacklem .fhsize = 0, 860220739Srmacklem .flags = NFSMNT_RESVPORT, 861191783Srmacklem .wsize = NFS_WSIZE, 862191783Srmacklem .rsize = NFS_RSIZE, 863191783Srmacklem .readdirsize = NFS_READDIRSIZE, 864191783Srmacklem .timeo = 10, 865191783Srmacklem .retrans = NFS_RETRANS, 866191783Srmacklem .readahead = NFS_DEFRAHEAD, 867191783Srmacklem .wcommitsize = 0, /* was: NQ_DEFLEASE */ 868191783Srmacklem .hostname = NULL, 869191783Srmacklem .acregmin = NFS_MINATTRTIMO, 870191783Srmacklem .acregmax = NFS_MAXATTRTIMO, 871191783Srmacklem .acdirmin = NFS_MINDIRATTRTIMO, 872191783Srmacklem .acdirmax = NFS_MAXDIRATTRTIMO, 873191783Srmacklem }; 874192585Srmacklem int error = 0, ret, len; 875192585Srmacklem struct sockaddr *nam = NULL; 876191783Srmacklem struct vnode *vp; 877191990Sattilio struct thread *td; 878191783Srmacklem char hst[MNAMELEN]; 879191783Srmacklem u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100]; 880285113Srmacklem char *cp, *opt, *name, *secname; 881230547Sjhb int nametimeo = NFS_DEFAULT_NAMETIMEO; 882203303Srmacklem int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO; 883244042Srmacklem int minvers = 0; 884299848Strasz int dirlen, has_nfs_args_opt, has_nfs_from_opt, 885299848Strasz krbnamelen, srvkrbnamelen; 886221205Srmacklem size_t hstlen; 887191783Srmacklem 888221190Srmacklem has_nfs_args_opt = 0; 889299848Strasz has_nfs_from_opt = 0; 890191783Srmacklem if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) { 891191783Srmacklem error = EINVAL; 892191783Srmacklem goto out; 893191783Srmacklem } 894191783Srmacklem 895191990Sattilio td = curthread; 896299848Strasz if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS && 897299848Strasz nfs_diskless_valid != 0) { 898221040Srmacklem error = nfs_mountroot(mp); 899191783Srmacklem goto out; 900191783Srmacklem } 901191783Srmacklem 902192585Srmacklem nfscl_init(); 903191783Srmacklem 904221190Srmacklem /* 905221190Srmacklem * The old mount_nfs program passed the struct nfs_args 906221190Srmacklem * from userspace to kernel. The new mount_nfs program 907221190Srmacklem * passes string options via nmount() from userspace to kernel 908221190Srmacklem * and we populate the struct nfs_args in the kernel. 909221190Srmacklem */ 910221190Srmacklem if (vfs_getopt(mp->mnt_optnew, "nfs_args", NULL, NULL) == 0) { 911221190Srmacklem error = vfs_copyopt(mp->mnt_optnew, "nfs_args", &args, 912221190Srmacklem sizeof(args)); 913221190Srmacklem if (error != 0) 914221190Srmacklem goto out; 915221190Srmacklem 916221190Srmacklem if (args.version != NFS_ARGSVERSION) { 917221190Srmacklem error = EPROGMISMATCH; 918221190Srmacklem goto out; 919221190Srmacklem } 920221190Srmacklem has_nfs_args_opt = 1; 921221190Srmacklem } 922221190Srmacklem 923192585Srmacklem /* Handle the new style options. */ 924273849Strasz if (vfs_getopt(mp->mnt_optnew, "noac", NULL, NULL) == 0) { 925273849Strasz args.acdirmin = args.acdirmax = 926273849Strasz args.acregmin = args.acregmax = 0; 927273849Strasz args.flags |= NFSMNT_ACDIRMIN | NFSMNT_ACDIRMAX | 928273849Strasz NFSMNT_ACREGMIN | NFSMNT_ACREGMAX; 929273849Strasz } 930192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0) 931192585Srmacklem args.flags |= NFSMNT_NOCONN; 932192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0) 933273852Strasz args.flags &= ~NFSMNT_NOCONN; 934192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0) 935192585Srmacklem args.flags |= NFSMNT_NOLOCKD; 936192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0) 937192585Srmacklem args.flags &= ~NFSMNT_NOLOCKD; 938192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0) 939192585Srmacklem args.flags |= NFSMNT_INT; 940192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0) 941192585Srmacklem args.flags |= NFSMNT_RDIRPLUS; 942192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0) 943192585Srmacklem args.flags |= NFSMNT_RESVPORT; 944192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0) 945192585Srmacklem args.flags &= ~NFSMNT_RESVPORT; 946192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0) 947192585Srmacklem args.flags |= NFSMNT_SOFT; 948192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0) 949192585Srmacklem args.flags &= ~NFSMNT_SOFT; 950192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0) 951192585Srmacklem args.sotype = SOCK_DGRAM; 952192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0) 953192585Srmacklem args.sotype = SOCK_DGRAM; 954192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0) 955192585Srmacklem args.sotype = SOCK_STREAM; 956192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0) 957192585Srmacklem args.flags |= NFSMNT_NFSV3; 958192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "nfsv4", NULL, NULL) == 0) { 959192585Srmacklem args.flags |= NFSMNT_NFSV4; 960192585Srmacklem args.sotype = SOCK_STREAM; 961191783Srmacklem } 962192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "allgssname", NULL, NULL) == 0) 963192585Srmacklem args.flags |= NFSMNT_ALLGSSNAME; 964221436Sru if (vfs_getopt(mp->mnt_optnew, "nocto", NULL, NULL) == 0) 965221436Sru args.flags |= NFSMNT_NOCTO; 966259084Srmacklem if (vfs_getopt(mp->mnt_optnew, "noncontigwr", NULL, NULL) == 0) 967259084Srmacklem args.flags |= NFSMNT_NONCONTIGWR; 968244042Srmacklem if (vfs_getopt(mp->mnt_optnew, "pnfs", NULL, NULL) == 0) 969244042Srmacklem args.flags |= NFSMNT_PNFS; 970192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) { 971192585Srmacklem if (opt == NULL) { 972192585Srmacklem vfs_mount_error(mp, "illegal readdirsize"); 973192585Srmacklem error = EINVAL; 974192585Srmacklem goto out; 975192585Srmacklem } 976192585Srmacklem ret = sscanf(opt, "%d", &args.readdirsize); 977192585Srmacklem if (ret != 1 || args.readdirsize <= 0) { 978192585Srmacklem vfs_mount_error(mp, "illegal readdirsize: %s", 979192585Srmacklem opt); 980192585Srmacklem error = EINVAL; 981192585Srmacklem goto out; 982192585Srmacklem } 983192585Srmacklem args.flags |= NFSMNT_READDIRSIZE; 984192585Srmacklem } 985192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) { 986192585Srmacklem if (opt == NULL) { 987192585Srmacklem vfs_mount_error(mp, "illegal readahead"); 988192585Srmacklem error = EINVAL; 989192585Srmacklem goto out; 990192585Srmacklem } 991192585Srmacklem ret = sscanf(opt, "%d", &args.readahead); 992192585Srmacklem if (ret != 1 || args.readahead <= 0) { 993192585Srmacklem vfs_mount_error(mp, "illegal readahead: %s", 994192585Srmacklem opt); 995192585Srmacklem error = EINVAL; 996192585Srmacklem goto out; 997192585Srmacklem } 998192585Srmacklem args.flags |= NFSMNT_READAHEAD; 999192585Srmacklem } 1000192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) { 1001192585Srmacklem if (opt == NULL) { 1002192585Srmacklem vfs_mount_error(mp, "illegal wsize"); 1003192585Srmacklem error = EINVAL; 1004192585Srmacklem goto out; 1005192585Srmacklem } 1006192585Srmacklem ret = sscanf(opt, "%d", &args.wsize); 1007192585Srmacklem if (ret != 1 || args.wsize <= 0) { 1008192585Srmacklem vfs_mount_error(mp, "illegal wsize: %s", 1009192585Srmacklem opt); 1010192585Srmacklem error = EINVAL; 1011192585Srmacklem goto out; 1012192585Srmacklem } 1013192585Srmacklem args.flags |= NFSMNT_WSIZE; 1014192585Srmacklem } 1015192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) { 1016192585Srmacklem if (opt == NULL) { 1017192585Srmacklem vfs_mount_error(mp, "illegal rsize"); 1018192585Srmacklem error = EINVAL; 1019192585Srmacklem goto out; 1020192585Srmacklem } 1021192585Srmacklem ret = sscanf(opt, "%d", &args.rsize); 1022192585Srmacklem if (ret != 1 || args.rsize <= 0) { 1023192585Srmacklem vfs_mount_error(mp, "illegal wsize: %s", 1024192585Srmacklem opt); 1025192585Srmacklem error = EINVAL; 1026192585Srmacklem goto out; 1027192585Srmacklem } 1028192585Srmacklem args.flags |= NFSMNT_RSIZE; 1029192585Srmacklem } 1030192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) { 1031192585Srmacklem if (opt == NULL) { 1032192585Srmacklem vfs_mount_error(mp, "illegal retrans"); 1033192585Srmacklem error = EINVAL; 1034192585Srmacklem goto out; 1035192585Srmacklem } 1036192585Srmacklem ret = sscanf(opt, "%d", &args.retrans); 1037192585Srmacklem if (ret != 1 || args.retrans <= 0) { 1038192585Srmacklem vfs_mount_error(mp, "illegal retrans: %s", 1039192585Srmacklem opt); 1040192585Srmacklem error = EINVAL; 1041192585Srmacklem goto out; 1042192585Srmacklem } 1043192585Srmacklem args.flags |= NFSMNT_RETRANS; 1044192585Srmacklem } 1045273849Strasz if (vfs_getopt(mp->mnt_optnew, "actimeo", (void **)&opt, NULL) == 0) { 1046273849Strasz ret = sscanf(opt, "%d", &args.acregmin); 1047273849Strasz if (ret != 1 || args.acregmin < 0) { 1048273849Strasz vfs_mount_error(mp, "illegal actimeo: %s", 1049273849Strasz opt); 1050273849Strasz error = EINVAL; 1051273849Strasz goto out; 1052273849Strasz } 1053273849Strasz args.acdirmin = args.acdirmax = args.acregmax = args.acregmin; 1054273849Strasz args.flags |= NFSMNT_ACDIRMIN | NFSMNT_ACDIRMAX | 1055273849Strasz NFSMNT_ACREGMIN | NFSMNT_ACREGMAX; 1056273849Strasz } 1057192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) { 1058192585Srmacklem ret = sscanf(opt, "%d", &args.acregmin); 1059192585Srmacklem if (ret != 1 || args.acregmin < 0) { 1060192585Srmacklem vfs_mount_error(mp, "illegal acregmin: %s", 1061192585Srmacklem opt); 1062192585Srmacklem error = EINVAL; 1063192585Srmacklem goto out; 1064192585Srmacklem } 1065192585Srmacklem args.flags |= NFSMNT_ACREGMIN; 1066192585Srmacklem } 1067192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) { 1068192585Srmacklem ret = sscanf(opt, "%d", &args.acregmax); 1069192585Srmacklem if (ret != 1 || args.acregmax < 0) { 1070192585Srmacklem vfs_mount_error(mp, "illegal acregmax: %s", 1071192585Srmacklem opt); 1072192585Srmacklem error = EINVAL; 1073192585Srmacklem goto out; 1074192585Srmacklem } 1075192585Srmacklem args.flags |= NFSMNT_ACREGMAX; 1076192585Srmacklem } 1077192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) { 1078192585Srmacklem ret = sscanf(opt, "%d", &args.acdirmin); 1079192585Srmacklem if (ret != 1 || args.acdirmin < 0) { 1080192585Srmacklem vfs_mount_error(mp, "illegal acdirmin: %s", 1081192585Srmacklem opt); 1082192585Srmacklem error = EINVAL; 1083192585Srmacklem goto out; 1084192585Srmacklem } 1085192585Srmacklem args.flags |= NFSMNT_ACDIRMIN; 1086192585Srmacklem } 1087192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) { 1088192585Srmacklem ret = sscanf(opt, "%d", &args.acdirmax); 1089192585Srmacklem if (ret != 1 || args.acdirmax < 0) { 1090192585Srmacklem vfs_mount_error(mp, "illegal acdirmax: %s", 1091192585Srmacklem opt); 1092192585Srmacklem error = EINVAL; 1093192585Srmacklem goto out; 1094192585Srmacklem } 1095192585Srmacklem args.flags |= NFSMNT_ACDIRMAX; 1096192585Srmacklem } 1097227507Sjhb if (vfs_getopt(mp->mnt_optnew, "wcommitsize", (void **)&opt, NULL) == 0) { 1098227507Sjhb ret = sscanf(opt, "%d", &args.wcommitsize); 1099227507Sjhb if (ret != 1 || args.wcommitsize < 0) { 1100227507Sjhb vfs_mount_error(mp, "illegal wcommitsize: %s", opt); 1101227507Sjhb error = EINVAL; 1102227507Sjhb goto out; 1103227507Sjhb } 1104227507Sjhb args.flags |= NFSMNT_WCOMMITSIZE; 1105227507Sjhb } 1106273849Strasz if (vfs_getopt(mp->mnt_optnew, "timeo", (void **)&opt, NULL) == 0) { 1107273849Strasz ret = sscanf(opt, "%d", &args.timeo); 1108273849Strasz if (ret != 1 || args.timeo <= 0) { 1109273849Strasz vfs_mount_error(mp, "illegal timeo: %s", 1110273849Strasz opt); 1111273849Strasz error = EINVAL; 1112273849Strasz goto out; 1113273849Strasz } 1114273849Strasz args.flags |= NFSMNT_TIMEO; 1115273849Strasz } 1116192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) { 1117192585Srmacklem ret = sscanf(opt, "%d", &args.timeo); 1118192585Srmacklem if (ret != 1 || args.timeo <= 0) { 1119192585Srmacklem vfs_mount_error(mp, "illegal timeout: %s", 1120192585Srmacklem opt); 1121192585Srmacklem error = EINVAL; 1122192585Srmacklem goto out; 1123192585Srmacklem } 1124192585Srmacklem args.flags |= NFSMNT_TIMEO; 1125192585Srmacklem } 1126230547Sjhb if (vfs_getopt(mp->mnt_optnew, "nametimeo", (void **)&opt, NULL) == 0) { 1127230547Sjhb ret = sscanf(opt, "%d", &nametimeo); 1128230547Sjhb if (ret != 1 || nametimeo < 0) { 1129230547Sjhb vfs_mount_error(mp, "illegal nametimeo: %s", opt); 1130230547Sjhb error = EINVAL; 1131230547Sjhb goto out; 1132230547Sjhb } 1133230547Sjhb } 1134203303Srmacklem if (vfs_getopt(mp->mnt_optnew, "negnametimeo", (void **)&opt, NULL) 1135203303Srmacklem == 0) { 1136203303Srmacklem ret = sscanf(opt, "%d", &negnametimeo); 1137203303Srmacklem if (ret != 1 || negnametimeo < 0) { 1138203303Srmacklem vfs_mount_error(mp, "illegal negnametimeo: %s", 1139203303Srmacklem opt); 1140203303Srmacklem error = EINVAL; 1141203303Srmacklem goto out; 1142203303Srmacklem } 1143203303Srmacklem } 1144244042Srmacklem if (vfs_getopt(mp->mnt_optnew, "minorversion", (void **)&opt, NULL) == 1145244042Srmacklem 0) { 1146244042Srmacklem ret = sscanf(opt, "%d", &minvers); 1147244042Srmacklem if (ret != 1 || minvers < 0 || minvers > 1 || 1148244042Srmacklem (args.flags & NFSMNT_NFSV4) == 0) { 1149244042Srmacklem vfs_mount_error(mp, "illegal minorversion: %s", opt); 1150244042Srmacklem error = EINVAL; 1151244042Srmacklem goto out; 1152244042Srmacklem } 1153244042Srmacklem } 1154192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "sec", 1155192585Srmacklem (void **) &secname, NULL) == 0) 1156192585Srmacklem nfs_sec_name(secname, &args.flags); 1157191783Srmacklem 1158191783Srmacklem if (mp->mnt_flag & MNT_UPDATE) { 1159191783Srmacklem struct nfsmount *nmp = VFSTONFS(mp); 1160191783Srmacklem 1161191783Srmacklem if (nmp == NULL) { 1162191783Srmacklem error = EIO; 1163191783Srmacklem goto out; 1164191783Srmacklem } 1165230803Srmacklem 1166191783Srmacklem /* 1167230803Srmacklem * If a change from TCP->UDP is done and there are thread(s) 1168298788Spfg * that have I/O RPC(s) in progress with a transfer size 1169230803Srmacklem * greater than NFS_MAXDGRAMDATA, those thread(s) will be 1170230803Srmacklem * hung, retrying the RPC(s) forever. Usually these threads 1171230803Srmacklem * will be seen doing an uninterruptible sleep on wait channel 1172276096Srmacklem * "nfsreq". 1173230803Srmacklem */ 1174230803Srmacklem if (args.sotype == SOCK_DGRAM && nmp->nm_sotype == SOCK_STREAM) 1175230803Srmacklem tprintf(td->td_proc, LOG_WARNING, 1176230803Srmacklem "Warning: mount -u that changes TCP->UDP can result in hung threads\n"); 1177230803Srmacklem 1178230803Srmacklem /* 1179191783Srmacklem * When doing an update, we can't change version, 1180191783Srmacklem * security, switch lockd strategies or change cookie 1181191783Srmacklem * translation 1182191783Srmacklem */ 1183191783Srmacklem args.flags = (args.flags & 1184191783Srmacklem ~(NFSMNT_NFSV3 | 1185191783Srmacklem NFSMNT_NFSV4 | 1186191783Srmacklem NFSMNT_KERB | 1187191783Srmacklem NFSMNT_INTEGRITY | 1188191783Srmacklem NFSMNT_PRIVACY | 1189191783Srmacklem NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) | 1190191783Srmacklem (nmp->nm_flag & 1191191783Srmacklem (NFSMNT_NFSV3 | 1192191783Srmacklem NFSMNT_NFSV4 | 1193191783Srmacklem NFSMNT_KERB | 1194191783Srmacklem NFSMNT_INTEGRITY | 1195191783Srmacklem NFSMNT_PRIVACY | 1196191783Srmacklem NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)); 1197214048Srmacklem nfs_decode_args(mp, nmp, &args, NULL, td->td_ucred, td); 1198191783Srmacklem goto out; 1199191783Srmacklem } 1200191783Srmacklem 1201191783Srmacklem /* 1202191783Srmacklem * Make the nfs_ip_paranoia sysctl serve as the default connection 1203191783Srmacklem * or no-connection mode for those protocols that support 1204191783Srmacklem * no-connection mode (the flag will be cleared later for protocols 1205191783Srmacklem * that do not support no-connection mode). This will allow a client 1206191783Srmacklem * to receive replies from a different IP then the request was 1207191783Srmacklem * sent to. Note: default value for nfs_ip_paranoia is 1 (paranoid), 1208191783Srmacklem * not 0. 1209191783Srmacklem */ 1210191783Srmacklem if (nfs_ip_paranoia == 0) 1211191783Srmacklem args.flags |= NFSMNT_NOCONN; 1212192585Srmacklem 1213221190Srmacklem if (has_nfs_args_opt != 0) { 1214221190Srmacklem /* 1215221190Srmacklem * In the 'nfs_args' case, the pointers in the args 1216221190Srmacklem * structure are in userland - we copy them in here. 1217221190Srmacklem */ 1218221190Srmacklem if (args.fhsize < 0 || args.fhsize > NFSX_V3FHMAX) { 1219192585Srmacklem vfs_mount_error(mp, "Bad file handle"); 1220191783Srmacklem error = EINVAL; 1221191783Srmacklem goto out; 1222191783Srmacklem } 1223221190Srmacklem error = copyin((caddr_t)args.fh, (caddr_t)nfh, 1224221190Srmacklem args.fhsize); 1225221190Srmacklem if (error != 0) 1226221190Srmacklem goto out; 1227221205Srmacklem error = copyinstr(args.hostname, hst, MNAMELEN - 1, &hstlen); 1228221190Srmacklem if (error != 0) 1229221190Srmacklem goto out; 1230221205Srmacklem bzero(&hst[hstlen], MNAMELEN - hstlen); 1231221190Srmacklem args.hostname = hst; 1232300160Sglebius /* getsockaddr() call must be after above copyin() calls */ 1233221190Srmacklem error = getsockaddr(&nam, (caddr_t)args.addr, 1234221190Srmacklem args.addrlen); 1235221190Srmacklem if (error != 0) 1236221190Srmacklem goto out; 1237299848Strasz } else if (nfs_mount_parse_from(mp->mnt_optnew, 1238299848Strasz &args.hostname, (struct sockaddr_in **)&nam, dirpath, 1239299848Strasz sizeof(dirpath), &dirlen) == 0) { 1240299848Strasz has_nfs_from_opt = 1; 1241299848Strasz bcopy(args.hostname, hst, MNAMELEN); 1242299848Strasz hst[MNAMELEN - 1] = '\0'; 1243299848Strasz 1244299848Strasz /* 1245299848Strasz * This only works with NFSv4 for now. 1246299848Strasz */ 1247299848Strasz args.fhsize = 0; 1248299848Strasz args.flags |= NFSMNT_NFSV4; 1249299848Strasz args.sotype = SOCK_STREAM; 1250191783Srmacklem } else { 1251221190Srmacklem if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh, 1252221190Srmacklem &args.fhsize) == 0) { 1253221190Srmacklem if (args.fhsize < 0 || args.fhsize > NFSX_FHMAX) { 1254221190Srmacklem vfs_mount_error(mp, "Bad file handle"); 1255221190Srmacklem error = EINVAL; 1256221190Srmacklem goto out; 1257221190Srmacklem } 1258221190Srmacklem bcopy(args.fh, nfh, args.fhsize); 1259221190Srmacklem } else { 1260221190Srmacklem args.fhsize = 0; 1261221190Srmacklem } 1262221190Srmacklem (void) vfs_getopt(mp->mnt_optnew, "hostname", 1263221190Srmacklem (void **)&args.hostname, &len); 1264221190Srmacklem if (args.hostname == NULL) { 1265221190Srmacklem vfs_mount_error(mp, "Invalid hostname"); 1266221190Srmacklem error = EINVAL; 1267221190Srmacklem goto out; 1268221190Srmacklem } 1269221190Srmacklem bcopy(args.hostname, hst, MNAMELEN); 1270221190Srmacklem hst[MNAMELEN - 1] = '\0'; 1271192585Srmacklem } 1272192585Srmacklem 1273192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "principal", (void **)&name, NULL) == 0) 1274192585Srmacklem strlcpy(srvkrbname, name, sizeof (srvkrbname)); 1275285113Srmacklem else { 1276192585Srmacklem snprintf(srvkrbname, sizeof (srvkrbname), "nfs@%s", hst); 1277285113Srmacklem cp = strchr(srvkrbname, ':'); 1278285113Srmacklem if (cp != NULL) 1279285113Srmacklem *cp = '\0'; 1280285113Srmacklem } 1281221014Srmacklem srvkrbnamelen = strlen(srvkrbname); 1282192585Srmacklem 1283192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "gssname", (void **)&name, NULL) == 0) 1284192585Srmacklem strlcpy(krbname, name, sizeof (krbname)); 1285192585Srmacklem else 1286191783Srmacklem krbname[0] = '\0'; 1287221014Srmacklem krbnamelen = strlen(krbname); 1288192585Srmacklem 1289299848Strasz if (has_nfs_from_opt == 0) { 1290299848Strasz if (vfs_getopt(mp->mnt_optnew, 1291299848Strasz "dirpath", (void **)&name, NULL) == 0) 1292299848Strasz strlcpy(dirpath, name, sizeof (dirpath)); 1293299848Strasz else 1294299848Strasz dirpath[0] = '\0'; 1295299848Strasz dirlen = strlen(dirpath); 1296299848Strasz } 1297192585Srmacklem 1298299848Strasz if (has_nfs_args_opt == 0 && has_nfs_from_opt == 0) { 1299222075Srmacklem if (vfs_getopt(mp->mnt_optnew, "addr", 1300222075Srmacklem (void **)&args.addr, &args.addrlen) == 0) { 1301222075Srmacklem if (args.addrlen > SOCK_MAXADDRLEN) { 1302222075Srmacklem error = ENAMETOOLONG; 1303222075Srmacklem goto out; 1304222075Srmacklem } 1305222075Srmacklem nam = malloc(args.addrlen, M_SONAME, M_WAITOK); 1306222075Srmacklem bcopy(args.addr, nam, args.addrlen); 1307222075Srmacklem nam->sa_len = args.addrlen; 1308222075Srmacklem } else { 1309222075Srmacklem vfs_mount_error(mp, "No server address"); 1310222075Srmacklem error = EINVAL; 1311191783Srmacklem goto out; 1312191783Srmacklem } 1313191783Srmacklem } 1314192585Srmacklem 1315191783Srmacklem args.fh = nfh; 1316221014Srmacklem error = mountnfs(&args, mp, nam, hst, krbname, krbnamelen, dirpath, 1317221014Srmacklem dirlen, srvkrbname, srvkrbnamelen, &vp, td->td_ucred, td, 1318244042Srmacklem nametimeo, negnametimeo, minvers); 1319191783Srmacklemout: 1320191783Srmacklem if (!error) { 1321191783Srmacklem MNT_ILOCK(mp); 1322281562Srmacklem mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_NO_IOPF | 1323281562Srmacklem MNTK_USES_BCACHE; 1324191783Srmacklem MNT_IUNLOCK(mp); 1325191783Srmacklem } 1326191783Srmacklem return (error); 1327191783Srmacklem} 1328191783Srmacklem 1329191783Srmacklem 1330191783Srmacklem/* 1331191783Srmacklem * VFS Operations. 1332191783Srmacklem * 1333191783Srmacklem * mount system call 1334191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then 1335191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before 1336300160Sglebius * doing the getsockaddr() call because getsockaddr() allocates an mbuf and 1337191783Srmacklem * an error after that means that I have to release the mbuf. 1338191783Srmacklem */ 1339191783Srmacklem/* ARGSUSED */ 1340191783Srmacklemstatic int 1341230249Smckusicknfs_cmount(struct mntarg *ma, void *data, uint64_t flags) 1342191783Srmacklem{ 1343191783Srmacklem int error; 1344191783Srmacklem struct nfs_args args; 1345191783Srmacklem 1346191783Srmacklem error = copyin(data, &args, sizeof (struct nfs_args)); 1347191783Srmacklem if (error) 1348191783Srmacklem return error; 1349191783Srmacklem 1350191783Srmacklem ma = mount_arg(ma, "nfs_args", &args, sizeof args); 1351191783Srmacklem 1352191783Srmacklem error = kernel_mount(ma, flags); 1353191783Srmacklem return (error); 1354191783Srmacklem} 1355191783Srmacklem 1356191783Srmacklem/* 1357191783Srmacklem * Common code for mount and mountroot 1358191783Srmacklem */ 1359191783Srmacklemstatic int 1360191783Srmacklemmountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam, 1361221014Srmacklem char *hst, u_char *krbname, int krbnamelen, u_char *dirpath, int dirlen, 1362221014Srmacklem u_char *srvkrbname, int srvkrbnamelen, struct vnode **vpp, 1363244042Srmacklem struct ucred *cred, struct thread *td, int nametimeo, int negnametimeo, 1364244042Srmacklem int minvers) 1365191783Srmacklem{ 1366191783Srmacklem struct nfsmount *nmp; 1367191783Srmacklem struct nfsnode *np; 1368195762Srmacklem int error, trycnt, ret; 1369191783Srmacklem struct nfsvattr nfsva; 1370244042Srmacklem struct nfsclclient *clp; 1371244042Srmacklem struct nfsclds *dsp, *tdsp; 1372244042Srmacklem uint32_t lease; 1373191783Srmacklem static u_int64_t clval = 0; 1374191783Srmacklem 1375244042Srmacklem NFSCL_DEBUG(3, "in mnt\n"); 1376244042Srmacklem clp = NULL; 1377191783Srmacklem if (mp->mnt_flag & MNT_UPDATE) { 1378191783Srmacklem nmp = VFSTONFS(mp); 1379191783Srmacklem printf("%s: MNT_UPDATE is no longer handled here\n", __func__); 1380191783Srmacklem FREE(nam, M_SONAME); 1381191783Srmacklem return (0); 1382191783Srmacklem } else { 1383191783Srmacklem MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount) + 1384221014Srmacklem krbnamelen + dirlen + srvkrbnamelen + 2, 1385221014Srmacklem M_NEWNFSMNT, M_WAITOK | M_ZERO); 1386191783Srmacklem TAILQ_INIT(&nmp->nm_bufq); 1387191783Srmacklem if (clval == 0) 1388191783Srmacklem clval = (u_int64_t)nfsboottime.tv_sec; 1389191783Srmacklem nmp->nm_clval = clval++; 1390221014Srmacklem nmp->nm_krbnamelen = krbnamelen; 1391221014Srmacklem nmp->nm_dirpathlen = dirlen; 1392221014Srmacklem nmp->nm_srvkrbnamelen = srvkrbnamelen; 1393192675Srmacklem if (td->td_ucred->cr_uid != (uid_t)0) { 1394191783Srmacklem /* 1395192675Srmacklem * nm_uid is used to get KerberosV credentials for 1396192675Srmacklem * the nfsv4 state handling operations if there is 1397192675Srmacklem * no host based principal set. Use the uid of 1398192675Srmacklem * this user if not root, since they are doing the 1399192675Srmacklem * mount. I don't think setting this for root will 1400192675Srmacklem * work, since root normally does not have user 1401192675Srmacklem * credentials in a credentials cache. 1402191783Srmacklem */ 1403192675Srmacklem nmp->nm_uid = td->td_ucred->cr_uid; 1404191783Srmacklem } else { 1405191783Srmacklem /* 1406192675Srmacklem * Just set to -1, so it won't be used. 1407191783Srmacklem */ 1408191783Srmacklem nmp->nm_uid = (uid_t)-1; 1409191783Srmacklem } 1410191783Srmacklem 1411191783Srmacklem /* Copy and null terminate all the names */ 1412191783Srmacklem if (nmp->nm_krbnamelen > 0) { 1413191783Srmacklem bcopy(krbname, nmp->nm_krbname, nmp->nm_krbnamelen); 1414191783Srmacklem nmp->nm_name[nmp->nm_krbnamelen] = '\0'; 1415191783Srmacklem } 1416191783Srmacklem if (nmp->nm_dirpathlen > 0) { 1417191783Srmacklem bcopy(dirpath, NFSMNT_DIRPATH(nmp), 1418191783Srmacklem nmp->nm_dirpathlen); 1419191783Srmacklem nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen 1420191783Srmacklem + 1] = '\0'; 1421191783Srmacklem } 1422191783Srmacklem if (nmp->nm_srvkrbnamelen > 0) { 1423191783Srmacklem bcopy(srvkrbname, NFSMNT_SRVKRBNAME(nmp), 1424191783Srmacklem nmp->nm_srvkrbnamelen); 1425191783Srmacklem nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen 1426191783Srmacklem + nmp->nm_srvkrbnamelen + 2] = '\0'; 1427191783Srmacklem } 1428191783Srmacklem nmp->nm_sockreq.nr_cred = crhold(cred); 1429191783Srmacklem mtx_init(&nmp->nm_sockreq.nr_mtx, "nfssock", NULL, MTX_DEF); 1430191783Srmacklem mp->mnt_data = nmp; 1431214048Srmacklem nmp->nm_getinfo = nfs_getnlminfo; 1432216931Srmacklem nmp->nm_vinvalbuf = ncl_vinvalbuf; 1433191783Srmacklem } 1434191783Srmacklem vfs_getnewfsid(mp); 1435191783Srmacklem nmp->nm_mountp = mp; 1436230547Sjhb mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK); 1437227493Srmacklem 1438227493Srmacklem /* 1439230547Sjhb * Since nfs_decode_args() might optionally set them, these 1440230547Sjhb * need to be set to defaults before the call, so that the 1441230547Sjhb * optional settings aren't overwritten. 1442227493Srmacklem */ 1443230547Sjhb nmp->nm_nametimeo = nametimeo; 1444203303Srmacklem nmp->nm_negnametimeo = negnametimeo; 1445227493Srmacklem nmp->nm_timeo = NFS_TIMEO; 1446227493Srmacklem nmp->nm_retry = NFS_RETRANS; 1447227493Srmacklem nmp->nm_readahead = NFS_DEFRAHEAD; 1448281738Smav 1449281738Smav /* This is empirical approximation of sqrt(hibufspace) * 256. */ 1450281738Smav nmp->nm_wcommitsize = NFS_MAXBSIZE / 256; 1451281738Smav while ((long)nmp->nm_wcommitsize * nmp->nm_wcommitsize < hibufspace) 1452281738Smav nmp->nm_wcommitsize *= 2; 1453281738Smav nmp->nm_wcommitsize *= 256; 1454281738Smav 1455244042Srmacklem if ((argp->flags & NFSMNT_NFSV4) != 0) 1456244042Srmacklem nmp->nm_minorvers = minvers; 1457244042Srmacklem else 1458244042Srmacklem nmp->nm_minorvers = 0; 1459191783Srmacklem 1460214048Srmacklem nfs_decode_args(mp, nmp, argp, hst, cred, td); 1461192585Srmacklem 1462191783Srmacklem /* 1463191783Srmacklem * V2 can only handle 32 bit filesizes. A 4GB-1 limit may be too 1464191783Srmacklem * high, depending on whether we end up with negative offsets in 1465191783Srmacklem * the client or server somewhere. 2GB-1 may be safer. 1466191783Srmacklem * 1467191783Srmacklem * For V3, ncl_fsinfo will adjust this as necessary. Assume maximum 1468191783Srmacklem * that we can handle until we find out otherwise. 1469191783Srmacklem */ 1470191783Srmacklem if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) 1471191783Srmacklem nmp->nm_maxfilesize = 0xffffffffLL; 1472191783Srmacklem else 1473221537Srmacklem nmp->nm_maxfilesize = OFF_MAX; 1474191783Srmacklem 1475191783Srmacklem if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) { 1476191783Srmacklem nmp->nm_wsize = NFS_WSIZE; 1477191783Srmacklem nmp->nm_rsize = NFS_RSIZE; 1478191783Srmacklem nmp->nm_readdirsize = NFS_READDIRSIZE; 1479191783Srmacklem } 1480191783Srmacklem nmp->nm_numgrps = NFS_MAXGRPS; 1481191783Srmacklem nmp->nm_tprintf_delay = nfs_tprintf_delay; 1482191783Srmacklem if (nmp->nm_tprintf_delay < 0) 1483191783Srmacklem nmp->nm_tprintf_delay = 0; 1484191783Srmacklem nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay; 1485191783Srmacklem if (nmp->nm_tprintf_initial_delay < 0) 1486191783Srmacklem nmp->nm_tprintf_initial_delay = 0; 1487191783Srmacklem nmp->nm_fhsize = argp->fhsize; 1488191783Srmacklem if (nmp->nm_fhsize > 0) 1489191783Srmacklem bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize); 1490191783Srmacklem bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN); 1491191783Srmacklem nmp->nm_nam = nam; 1492191783Srmacklem /* Set up the sockets and per-host congestion */ 1493191783Srmacklem nmp->nm_sotype = argp->sotype; 1494191783Srmacklem nmp->nm_soproto = argp->proto; 1495191783Srmacklem nmp->nm_sockreq.nr_prog = NFS_PROG; 1496191783Srmacklem if ((argp->flags & NFSMNT_NFSV4)) 1497191783Srmacklem nmp->nm_sockreq.nr_vers = NFS_VER4; 1498191783Srmacklem else if ((argp->flags & NFSMNT_NFSV3)) 1499191783Srmacklem nmp->nm_sockreq.nr_vers = NFS_VER3; 1500191783Srmacklem else 1501191783Srmacklem nmp->nm_sockreq.nr_vers = NFS_VER2; 1502191783Srmacklem 1503191783Srmacklem 1504191783Srmacklem if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0))) 1505191783Srmacklem goto bad; 1506244042Srmacklem /* For NFSv4.1, get the clientid now. */ 1507244042Srmacklem if (nmp->nm_minorvers > 0) { 1508244042Srmacklem NFSCL_DEBUG(3, "at getcl\n"); 1509244042Srmacklem error = nfscl_getcl(mp, cred, td, 0, &clp); 1510244042Srmacklem NFSCL_DEBUG(3, "aft getcl=%d\n", error); 1511244042Srmacklem if (error != 0) 1512244042Srmacklem goto bad; 1513244042Srmacklem } 1514191783Srmacklem 1515191783Srmacklem if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) && 1516191783Srmacklem nmp->nm_dirpathlen > 0) { 1517244042Srmacklem NFSCL_DEBUG(3, "in dirp\n"); 1518191783Srmacklem /* 1519191783Srmacklem * If the fhsize on the mount point == 0 for V4, the mount 1520191783Srmacklem * path needs to be looked up. 1521191783Srmacklem */ 1522191783Srmacklem trycnt = 3; 1523191783Srmacklem do { 1524191783Srmacklem error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp), 1525191783Srmacklem cred, td); 1526244042Srmacklem NFSCL_DEBUG(3, "aft dirp=%d\n", error); 1527191783Srmacklem if (error) 1528207170Srmacklem (void) nfs_catnap(PZERO, error, "nfsgetdirp"); 1529191783Srmacklem } while (error && --trycnt > 0); 1530191783Srmacklem if (error) { 1531191783Srmacklem error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); 1532191783Srmacklem goto bad; 1533191783Srmacklem } 1534191783Srmacklem } 1535244042Srmacklem 1536244042Srmacklem /* 1537244042Srmacklem * A reference count is needed on the nfsnode representing the 1538244042Srmacklem * remote root. If this object is not persistent, then backward 1539244042Srmacklem * traversals of the mount point (i.e. "..") will not work if 1540244042Srmacklem * the nfsnode gets flushed out of the cache. Ufs does not have 1541244042Srmacklem * this problem, because one can identify root inodes by their 1542244042Srmacklem * number == ROOTINO (2). 1543244042Srmacklem */ 1544191783Srmacklem if (nmp->nm_fhsize > 0) { 1545195762Srmacklem /* 1546195762Srmacklem * Set f_iosize to NFS_DIRBLKSIZ so that bo_bsize gets set 1547195762Srmacklem * non-zero for the root vnode. f_iosize will be set correctly 1548195762Srmacklem * by nfs_statfs() before any I/O occurs. 1549195762Srmacklem */ 1550195762Srmacklem mp->mnt_stat.f_iosize = NFS_DIRBLKSIZ; 1551220732Srmacklem error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, 1552220732Srmacklem LK_EXCLUSIVE); 1553191783Srmacklem if (error) 1554191783Srmacklem goto bad; 1555191783Srmacklem *vpp = NFSTOV(np); 1556191783Srmacklem 1557191783Srmacklem /* 1558191783Srmacklem * Get file attributes and transfer parameters for the 1559191783Srmacklem * mountpoint. This has the side effect of filling in 1560191783Srmacklem * (*vpp)->v_type with the correct value. 1561191783Srmacklem */ 1562191783Srmacklem ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1, 1563244042Srmacklem cred, td, &nfsva, NULL, &lease); 1564191783Srmacklem if (ret) { 1565191783Srmacklem /* 1566191783Srmacklem * Just set default values to get things going. 1567191783Srmacklem */ 1568191783Srmacklem NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr)); 1569191783Srmacklem nfsva.na_vattr.va_type = VDIR; 1570191783Srmacklem nfsva.na_vattr.va_mode = 0777; 1571191783Srmacklem nfsva.na_vattr.va_nlink = 100; 1572191783Srmacklem nfsva.na_vattr.va_uid = (uid_t)0; 1573191783Srmacklem nfsva.na_vattr.va_gid = (gid_t)0; 1574191783Srmacklem nfsva.na_vattr.va_fileid = 2; 1575191783Srmacklem nfsva.na_vattr.va_gen = 1; 1576191783Srmacklem nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE; 1577191783Srmacklem nfsva.na_vattr.va_size = 512 * 1024; 1578244042Srmacklem lease = 60; 1579191783Srmacklem } 1580191783Srmacklem (void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1); 1581244042Srmacklem if (nmp->nm_minorvers > 0) { 1582244042Srmacklem NFSCL_DEBUG(3, "lease=%d\n", (int)lease); 1583244042Srmacklem NFSLOCKCLSTATE(); 1584244042Srmacklem clp->nfsc_renew = NFSCL_RENEW(lease); 1585244042Srmacklem clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew; 1586244042Srmacklem clp->nfsc_clientidrev++; 1587244042Srmacklem if (clp->nfsc_clientidrev == 0) 1588244042Srmacklem clp->nfsc_clientidrev++; 1589244042Srmacklem NFSUNLOCKCLSTATE(); 1590244042Srmacklem /* 1591244042Srmacklem * Mount will succeed, so the renew thread can be 1592244042Srmacklem * started now. 1593244042Srmacklem */ 1594244042Srmacklem nfscl_start_renewthread(clp); 1595244042Srmacklem nfscl_clientrelease(clp); 1596244042Srmacklem } 1597191783Srmacklem if (argp->flags & NFSMNT_NFSV3) 1598191783Srmacklem ncl_fsinfo(nmp, *vpp, cred, td); 1599191783Srmacklem 1600222233Srmacklem /* Mark if the mount point supports NFSv4 ACLs. */ 1601222233Srmacklem if ((argp->flags & NFSMNT_NFSV4) != 0 && nfsrv_useacl != 0 && 1602222233Srmacklem ret == 0 && 1603222233Srmacklem NFSISSET_ATTRBIT(&nfsva.na_suppattr, NFSATTRBIT_ACL)) { 1604222233Srmacklem MNT_ILOCK(mp); 1605222233Srmacklem mp->mnt_flag |= MNT_NFS4ACLS; 1606222233Srmacklem MNT_IUNLOCK(mp); 1607222233Srmacklem } 1608222233Srmacklem 1609191783Srmacklem /* 1610191783Srmacklem * Lose the lock but keep the ref. 1611191783Srmacklem */ 1612224082Szack NFSVOPUNLOCK(*vpp, 0); 1613191783Srmacklem return (0); 1614191783Srmacklem } 1615191783Srmacklem error = EIO; 1616191783Srmacklem 1617191783Srmacklembad: 1618244042Srmacklem if (clp != NULL) 1619244042Srmacklem nfscl_clientrelease(clp); 1620191783Srmacklem newnfs_disconnect(&nmp->nm_sockreq); 1621191783Srmacklem crfree(nmp->nm_sockreq.nr_cred); 1622253049Srmacklem if (nmp->nm_sockreq.nr_auth != NULL) 1623253049Srmacklem AUTH_DESTROY(nmp->nm_sockreq.nr_auth); 1624191783Srmacklem mtx_destroy(&nmp->nm_sockreq.nr_mtx); 1625191783Srmacklem mtx_destroy(&nmp->nm_mtx); 1626244042Srmacklem if (nmp->nm_clp != NULL) { 1627244042Srmacklem NFSLOCKCLSTATE(); 1628244042Srmacklem LIST_REMOVE(nmp->nm_clp, nfsc_list); 1629244042Srmacklem NFSUNLOCKCLSTATE(); 1630244042Srmacklem free(nmp->nm_clp, M_NFSCLCLIENT); 1631244042Srmacklem } 1632244042Srmacklem TAILQ_FOREACH_SAFE(dsp, &nmp->nm_sess, nfsclds_list, tdsp) 1633244042Srmacklem nfscl_freenfsclds(dsp); 1634191783Srmacklem FREE(nmp, M_NEWNFSMNT); 1635191783Srmacklem FREE(nam, M_SONAME); 1636191783Srmacklem return (error); 1637191783Srmacklem} 1638191783Srmacklem 1639191783Srmacklem/* 1640191783Srmacklem * unmount system call 1641191783Srmacklem */ 1642191783Srmacklemstatic int 1643191990Sattilionfs_unmount(struct mount *mp, int mntflags) 1644191783Srmacklem{ 1645191990Sattilio struct thread *td; 1646191783Srmacklem struct nfsmount *nmp; 1647249630Srmacklem int error, flags = 0, i, trycnt = 0; 1648244042Srmacklem struct nfsclds *dsp, *tdsp; 1649191783Srmacklem 1650191990Sattilio td = curthread; 1651191990Sattilio 1652191783Srmacklem if (mntflags & MNT_FORCE) 1653191783Srmacklem flags |= FORCECLOSE; 1654191783Srmacklem nmp = VFSTONFS(mp); 1655191783Srmacklem /* 1656191783Srmacklem * Goes something like this.. 1657191783Srmacklem * - Call vflush() to clear out vnodes for this filesystem 1658191783Srmacklem * - Close the socket 1659191783Srmacklem * - Free up the data structures 1660191783Srmacklem */ 1661191783Srmacklem /* In the forced case, cancel any outstanding requests. */ 1662191783Srmacklem if (mntflags & MNT_FORCE) { 1663191783Srmacklem error = newnfs_nmcancelreqs(nmp); 1664191783Srmacklem if (error) 1665191783Srmacklem goto out; 1666191783Srmacklem /* For a forced close, get rid of the renew thread now */ 1667191783Srmacklem nfscl_umount(nmp, td); 1668191783Srmacklem } 1669191783Srmacklem /* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */ 1670191783Srmacklem do { 1671191783Srmacklem error = vflush(mp, 1, flags, td); 1672191783Srmacklem if ((mntflags & MNT_FORCE) && error != 0 && ++trycnt < 30) 1673207170Srmacklem (void) nfs_catnap(PSOCK, error, "newndm"); 1674191783Srmacklem } while ((mntflags & MNT_FORCE) && error != 0 && trycnt < 30); 1675191783Srmacklem if (error) 1676191783Srmacklem goto out; 1677191783Srmacklem 1678191783Srmacklem /* 1679191783Srmacklem * We are now committed to the unmount. 1680191783Srmacklem */ 1681191783Srmacklem if ((mntflags & MNT_FORCE) == 0) 1682191783Srmacklem nfscl_umount(nmp, td); 1683249630Srmacklem /* Make sure no nfsiods are assigned to this mount. */ 1684249630Srmacklem mtx_lock(&ncl_iod_mutex); 1685249630Srmacklem for (i = 0; i < NFS_MAXASYNCDAEMON; i++) 1686249630Srmacklem if (ncl_iodmount[i] == nmp) { 1687249630Srmacklem ncl_iodwant[i] = NFSIOD_AVAILABLE; 1688249630Srmacklem ncl_iodmount[i] = NULL; 1689249630Srmacklem } 1690249630Srmacklem mtx_unlock(&ncl_iod_mutex); 1691191783Srmacklem newnfs_disconnect(&nmp->nm_sockreq); 1692191783Srmacklem crfree(nmp->nm_sockreq.nr_cred); 1693191783Srmacklem FREE(nmp->nm_nam, M_SONAME); 1694253049Srmacklem if (nmp->nm_sockreq.nr_auth != NULL) 1695253049Srmacklem AUTH_DESTROY(nmp->nm_sockreq.nr_auth); 1696191783Srmacklem mtx_destroy(&nmp->nm_sockreq.nr_mtx); 1697191783Srmacklem mtx_destroy(&nmp->nm_mtx); 1698244042Srmacklem TAILQ_FOREACH_SAFE(dsp, &nmp->nm_sess, nfsclds_list, tdsp) 1699244042Srmacklem nfscl_freenfsclds(dsp); 1700191783Srmacklem FREE(nmp, M_NEWNFSMNT); 1701191783Srmacklemout: 1702191783Srmacklem return (error); 1703191783Srmacklem} 1704191783Srmacklem 1705191783Srmacklem/* 1706191783Srmacklem * Return root of a filesystem 1707191783Srmacklem */ 1708191783Srmacklemstatic int 1709191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp) 1710191783Srmacklem{ 1711191783Srmacklem struct vnode *vp; 1712191783Srmacklem struct nfsmount *nmp; 1713191783Srmacklem struct nfsnode *np; 1714191783Srmacklem int error; 1715191783Srmacklem 1716191783Srmacklem nmp = VFSTONFS(mp); 1717220732Srmacklem error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, flags); 1718191783Srmacklem if (error) 1719191783Srmacklem return error; 1720191783Srmacklem vp = NFSTOV(np); 1721191783Srmacklem /* 1722191783Srmacklem * Get transfer parameters and attributes for root vnode once. 1723191783Srmacklem */ 1724191783Srmacklem mtx_lock(&nmp->nm_mtx); 1725191783Srmacklem if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) { 1726191783Srmacklem mtx_unlock(&nmp->nm_mtx); 1727191783Srmacklem ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread); 1728191783Srmacklem } else 1729191783Srmacklem mtx_unlock(&nmp->nm_mtx); 1730191783Srmacklem if (vp->v_type == VNON) 1731191783Srmacklem vp->v_type = VDIR; 1732191783Srmacklem vp->v_vflag |= VV_ROOT; 1733191783Srmacklem *vpp = vp; 1734191783Srmacklem return (0); 1735191783Srmacklem} 1736191783Srmacklem 1737191783Srmacklem/* 1738191783Srmacklem * Flush out the buffer cache 1739191783Srmacklem */ 1740191783Srmacklem/* ARGSUSED */ 1741191783Srmacklemstatic int 1742191990Sattilionfs_sync(struct mount *mp, int waitfor) 1743191783Srmacklem{ 1744191783Srmacklem struct vnode *vp, *mvp; 1745191990Sattilio struct thread *td; 1746191783Srmacklem int error, allerror = 0; 1747191783Srmacklem 1748191990Sattilio td = curthread; 1749191990Sattilio 1750222329Srmacklem MNT_ILOCK(mp); 1751191783Srmacklem /* 1752222329Srmacklem * If a forced dismount is in progress, return from here so that 1753222329Srmacklem * the umount(2) syscall doesn't get stuck in VFS_SYNC() before 1754222329Srmacklem * calling VFS_UNMOUNT(). 1755222329Srmacklem */ 1756222329Srmacklem if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { 1757222329Srmacklem MNT_IUNLOCK(mp); 1758222329Srmacklem return (EBADF); 1759222329Srmacklem } 1760234386Smckusick MNT_IUNLOCK(mp); 1761222329Srmacklem 1762222329Srmacklem /* 1763191783Srmacklem * Force stale buffer cache information to be flushed. 1764191783Srmacklem */ 1765191783Srmacklemloop: 1766234386Smckusick MNT_VNODE_FOREACH_ALL(vp, mp, mvp) { 1767191783Srmacklem /* XXX Racy bv_cnt check. */ 1768224083Szack if (NFSVOPISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 || 1769191783Srmacklem waitfor == MNT_LAZY) { 1770191783Srmacklem VI_UNLOCK(vp); 1771191783Srmacklem continue; 1772191783Srmacklem } 1773191783Srmacklem if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) { 1774234386Smckusick MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp); 1775191783Srmacklem goto loop; 1776191783Srmacklem } 1777191783Srmacklem error = VOP_FSYNC(vp, waitfor, td); 1778191783Srmacklem if (error) 1779191783Srmacklem allerror = error; 1780224082Szack NFSVOPUNLOCK(vp, 0); 1781191783Srmacklem vrele(vp); 1782191783Srmacklem } 1783191783Srmacklem return (allerror); 1784191783Srmacklem} 1785191783Srmacklem 1786191783Srmacklemstatic int 1787191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req) 1788191783Srmacklem{ 1789191783Srmacklem struct nfsmount *nmp = VFSTONFS(mp); 1790191783Srmacklem struct vfsquery vq; 1791191783Srmacklem int error; 1792191783Srmacklem 1793191783Srmacklem bzero(&vq, sizeof(vq)); 1794191783Srmacklem switch (op) { 1795191783Srmacklem#if 0 1796191783Srmacklem case VFS_CTL_NOLOCKS: 1797191783Srmacklem val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0; 1798191783Srmacklem if (req->oldptr != NULL) { 1799191783Srmacklem error = SYSCTL_OUT(req, &val, sizeof(val)); 1800191783Srmacklem if (error) 1801191783Srmacklem return (error); 1802191783Srmacklem } 1803191783Srmacklem if (req->newptr != NULL) { 1804191783Srmacklem error = SYSCTL_IN(req, &val, sizeof(val)); 1805191783Srmacklem if (error) 1806191783Srmacklem return (error); 1807191783Srmacklem if (val) 1808191783Srmacklem nmp->nm_flag |= NFSMNT_NOLOCKS; 1809191783Srmacklem else 1810191783Srmacklem nmp->nm_flag &= ~NFSMNT_NOLOCKS; 1811191783Srmacklem } 1812191783Srmacklem break; 1813191783Srmacklem#endif 1814191783Srmacklem case VFS_CTL_QUERY: 1815191783Srmacklem mtx_lock(&nmp->nm_mtx); 1816191783Srmacklem if (nmp->nm_state & NFSSTA_TIMEO) 1817191783Srmacklem vq.vq_flags |= VQ_NOTRESP; 1818191783Srmacklem mtx_unlock(&nmp->nm_mtx); 1819191783Srmacklem#if 0 1820191783Srmacklem if (!(nmp->nm_flag & NFSMNT_NOLOCKS) && 1821191783Srmacklem (nmp->nm_state & NFSSTA_LOCKTIMEO)) 1822191783Srmacklem vq.vq_flags |= VQ_NOTRESPLOCK; 1823191783Srmacklem#endif 1824191783Srmacklem error = SYSCTL_OUT(req, &vq, sizeof(vq)); 1825191783Srmacklem break; 1826191783Srmacklem case VFS_CTL_TIMEO: 1827191783Srmacklem if (req->oldptr != NULL) { 1828191783Srmacklem error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay, 1829191783Srmacklem sizeof(nmp->nm_tprintf_initial_delay)); 1830191783Srmacklem if (error) 1831191783Srmacklem return (error); 1832191783Srmacklem } 1833191783Srmacklem if (req->newptr != NULL) { 1834191783Srmacklem error = vfs_suser(mp, req->td); 1835191783Srmacklem if (error) 1836191783Srmacklem return (error); 1837191783Srmacklem error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay, 1838191783Srmacklem sizeof(nmp->nm_tprintf_initial_delay)); 1839191783Srmacklem if (error) 1840191783Srmacklem return (error); 1841191783Srmacklem if (nmp->nm_tprintf_initial_delay < 0) 1842191783Srmacklem nmp->nm_tprintf_initial_delay = 0; 1843191783Srmacklem } 1844191783Srmacklem break; 1845191783Srmacklem default: 1846191783Srmacklem return (ENOTSUP); 1847191783Srmacklem } 1848191783Srmacklem return (0); 1849191783Srmacklem} 1850191783Srmacklem 1851214048Srmacklem/* 1852255136Srmacklem * Purge any RPCs in progress, so that they will all return errors. 1853255136Srmacklem * This allows dounmount() to continue as far as VFS_UNMOUNT() for a 1854255136Srmacklem * forced dismount. 1855255136Srmacklem */ 1856255136Srmacklemstatic void 1857255136Srmacklemnfs_purge(struct mount *mp) 1858255136Srmacklem{ 1859255136Srmacklem struct nfsmount *nmp = VFSTONFS(mp); 1860255136Srmacklem 1861255136Srmacklem newnfs_nmcancelreqs(nmp); 1862255136Srmacklem} 1863255136Srmacklem 1864255136Srmacklem/* 1865214048Srmacklem * Extract the information needed by the nlm from the nfs vnode. 1866214048Srmacklem */ 1867214048Srmacklemstatic void 1868214053Srmacklemnfs_getnlminfo(struct vnode *vp, uint8_t *fhp, size_t *fhlenp, 1869216931Srmacklem struct sockaddr_storage *sp, int *is_v3p, off_t *sizep, 1870216931Srmacklem struct timeval *timeop) 1871214048Srmacklem{ 1872214048Srmacklem struct nfsmount *nmp; 1873214048Srmacklem struct nfsnode *np = VTONFS(vp); 1874214048Srmacklem 1875214048Srmacklem nmp = VFSTONFS(vp->v_mount); 1876214048Srmacklem if (fhlenp != NULL) 1877214053Srmacklem *fhlenp = (size_t)np->n_fhp->nfh_len; 1878214048Srmacklem if (fhp != NULL) 1879214048Srmacklem bcopy(np->n_fhp->nfh_fh, fhp, np->n_fhp->nfh_len); 1880214048Srmacklem if (sp != NULL) 1881214048Srmacklem bcopy(nmp->nm_nam, sp, min(nmp->nm_nam->sa_len, sizeof(*sp))); 1882214048Srmacklem if (is_v3p != NULL) 1883214048Srmacklem *is_v3p = NFS_ISV3(vp); 1884214048Srmacklem if (sizep != NULL) 1885214048Srmacklem *sizep = np->n_size; 1886216931Srmacklem if (timeop != NULL) { 1887216931Srmacklem timeop->tv_sec = nmp->nm_timeo / NFS_HZ; 1888216931Srmacklem timeop->tv_usec = (nmp->nm_timeo % NFS_HZ) * (1000000 / NFS_HZ); 1889216931Srmacklem } 1890214048Srmacklem} 1891214048Srmacklem 1892243782Srmacklem/* 1893243782Srmacklem * This function prints out an option name, based on the conditional 1894243782Srmacklem * argument. 1895243782Srmacklem */ 1896243782Srmacklemstatic __inline void nfscl_printopt(struct nfsmount *nmp, int testval, 1897243782Srmacklem char *opt, char **buf, size_t *blen) 1898243782Srmacklem{ 1899243782Srmacklem int len; 1900243782Srmacklem 1901243782Srmacklem if (testval != 0 && *blen > strlen(opt)) { 1902243782Srmacklem len = snprintf(*buf, *blen, "%s", opt); 1903243782Srmacklem if (len != strlen(opt)) 1904243782Srmacklem printf("EEK!!\n"); 1905243782Srmacklem *buf += len; 1906243782Srmacklem *blen -= len; 1907243782Srmacklem } 1908243782Srmacklem} 1909243782Srmacklem 1910243782Srmacklem/* 1911243782Srmacklem * This function printf out an options integer value. 1912243782Srmacklem */ 1913243782Srmacklemstatic __inline void nfscl_printoptval(struct nfsmount *nmp, int optval, 1914243782Srmacklem char *opt, char **buf, size_t *blen) 1915243782Srmacklem{ 1916243782Srmacklem int len; 1917243782Srmacklem 1918243782Srmacklem if (*blen > strlen(opt) + 1) { 1919243782Srmacklem /* Could result in truncated output string. */ 1920243782Srmacklem len = snprintf(*buf, *blen, "%s=%d", opt, optval); 1921243782Srmacklem if (len < *blen) { 1922243782Srmacklem *buf += len; 1923243782Srmacklem *blen -= len; 1924243782Srmacklem } 1925243782Srmacklem } 1926243782Srmacklem} 1927243782Srmacklem 1928243782Srmacklem/* 1929243782Srmacklem * Load the option flags and values into the buffer. 1930243782Srmacklem */ 1931243782Srmacklemvoid nfscl_retopts(struct nfsmount *nmp, char *buffer, size_t buflen) 1932243782Srmacklem{ 1933243782Srmacklem char *buf; 1934243782Srmacklem size_t blen; 1935243782Srmacklem 1936243782Srmacklem buf = buffer; 1937243782Srmacklem blen = buflen; 1938243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NFSV4) != 0, "nfsv4", &buf, 1939243782Srmacklem &blen); 1940244056Srmacklem if ((nmp->nm_flag & NFSMNT_NFSV4) != 0) { 1941244056Srmacklem nfscl_printoptval(nmp, nmp->nm_minorvers, ",minorversion", &buf, 1942244056Srmacklem &blen); 1943244056Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_PNFS) != 0, ",pnfs", 1944244056Srmacklem &buf, &blen); 1945244056Srmacklem } 1946243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NFSV3) != 0, "nfsv3", &buf, 1947243782Srmacklem &blen); 1948243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0, 1949243782Srmacklem "nfsv2", &buf, &blen); 1950243782Srmacklem nfscl_printopt(nmp, nmp->nm_sotype == SOCK_STREAM, ",tcp", &buf, &blen); 1951243782Srmacklem nfscl_printopt(nmp, nmp->nm_sotype != SOCK_STREAM, ",udp", &buf, &blen); 1952243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_RESVPORT) != 0, ",resvport", 1953243782Srmacklem &buf, &blen); 1954243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCONN) != 0, ",noconn", 1955243782Srmacklem &buf, &blen); 1956243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_SOFT) == 0, ",hard", &buf, 1957243782Srmacklem &blen); 1958243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_SOFT) != 0, ",soft", &buf, 1959243782Srmacklem &blen); 1960243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_INT) != 0, ",intr", &buf, 1961243782Srmacklem &blen); 1962243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCTO) == 0, ",cto", &buf, 1963243782Srmacklem &blen); 1964243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NOCTO) != 0, ",nocto", &buf, 1965243782Srmacklem &blen); 1966259084Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_NONCONTIGWR) != 0, 1967259084Srmacklem ",noncontigwr", &buf, &blen); 1968243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_NOLOCKD | NFSMNT_NFSV4)) == 1969243782Srmacklem 0, ",lockd", &buf, &blen); 1970243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_NOLOCKD | NFSMNT_NFSV4)) == 1971243782Srmacklem NFSMNT_NOLOCKD, ",nolockd", &buf, &blen); 1972243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_RDIRPLUS) != 0, ",rdirplus", 1973243782Srmacklem &buf, &blen); 1974243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & NFSMNT_KERB) == 0, ",sec=sys", 1975243782Srmacklem &buf, &blen); 1976243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_KERB | NFSMNT_INTEGRITY | 1977243782Srmacklem NFSMNT_PRIVACY)) == NFSMNT_KERB, ",sec=krb5", &buf, &blen); 1978243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_KERB | NFSMNT_INTEGRITY | 1979243782Srmacklem NFSMNT_PRIVACY)) == (NFSMNT_KERB | NFSMNT_INTEGRITY), ",sec=krb5i", 1980243782Srmacklem &buf, &blen); 1981243782Srmacklem nfscl_printopt(nmp, (nmp->nm_flag & (NFSMNT_KERB | NFSMNT_INTEGRITY | 1982243782Srmacklem NFSMNT_PRIVACY)) == (NFSMNT_KERB | NFSMNT_PRIVACY), ",sec=krb5p", 1983243782Srmacklem &buf, &blen); 1984243782Srmacklem nfscl_printoptval(nmp, nmp->nm_acdirmin, ",acdirmin", &buf, &blen); 1985243782Srmacklem nfscl_printoptval(nmp, nmp->nm_acdirmax, ",acdirmax", &buf, &blen); 1986243782Srmacklem nfscl_printoptval(nmp, nmp->nm_acregmin, ",acregmin", &buf, &blen); 1987243782Srmacklem nfscl_printoptval(nmp, nmp->nm_acregmax, ",acregmax", &buf, &blen); 1988243782Srmacklem nfscl_printoptval(nmp, nmp->nm_nametimeo, ",nametimeo", &buf, &blen); 1989243782Srmacklem nfscl_printoptval(nmp, nmp->nm_negnametimeo, ",negnametimeo", &buf, 1990243782Srmacklem &blen); 1991243782Srmacklem nfscl_printoptval(nmp, nmp->nm_rsize, ",rsize", &buf, &blen); 1992243782Srmacklem nfscl_printoptval(nmp, nmp->nm_wsize, ",wsize", &buf, &blen); 1993243782Srmacklem nfscl_printoptval(nmp, nmp->nm_readdirsize, ",readdirsize", &buf, 1994243782Srmacklem &blen); 1995243782Srmacklem nfscl_printoptval(nmp, nmp->nm_readahead, ",readahead", &buf, &blen); 1996243782Srmacklem nfscl_printoptval(nmp, nmp->nm_wcommitsize, ",wcommitsize", &buf, 1997243782Srmacklem &blen); 1998243782Srmacklem nfscl_printoptval(nmp, nmp->nm_timeo, ",timeout", &buf, &blen); 1999243782Srmacklem nfscl_printoptval(nmp, nmp->nm_retry, ",retrans", &buf, &blen); 2000243782Srmacklem} 2001243782Srmacklem 2002