nfs_clvfsops.c revision 221537
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: head/sys/fs/nfsclient/nfs_clvfsops.c 221537 2011-05-06 17:51:00Z rmacklem $"); 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; 82191783Srmacklem 83191783SrmacklemMALLOC_DEFINE(M_NEWNFSREQ, "newnfsclient_req", "New NFS request header"); 84191783SrmacklemMALLOC_DEFINE(M_NEWNFSMNT, "newnfsmnt", "New NFS mount struct"); 85191783Srmacklem 86191783SrmacklemSYSCTL_DECL(_vfs_newnfs); 87191783SrmacklemSYSCTL_STRUCT(_vfs_newnfs, NFS_NFSSTATS, nfsstats, CTLFLAG_RW, 88191783Srmacklem &newnfsstats, nfsstats, "S,nfsstats"); 89191783Srmacklemstatic int nfs_ip_paranoia = 1; 90191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, nfs_ip_paranoia, CTLFLAG_RW, 91191783Srmacklem &nfs_ip_paranoia, 0, ""); 92191783Srmacklemstatic int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY; 93191783SrmacklemSYSCTL_INT(_vfs_newnfs, NFS_TPRINTF_INITIAL_DELAY, 94191783Srmacklem downdelayinitial, CTLFLAG_RW, &nfs_tprintf_initial_delay, 0, ""); 95191783Srmacklem/* how long between console messages "nfs server foo not responding" */ 96191783Srmacklemstatic int nfs_tprintf_delay = NFS_TPRINTF_DELAY; 97191783SrmacklemSYSCTL_INT(_vfs_newnfs, NFS_TPRINTF_DELAY, 98191783Srmacklem downdelayinterval, CTLFLAG_RW, &nfs_tprintf_delay, 0, ""); 99191783Srmacklem 100221040Srmacklemstatic int nfs_mountroot(struct mount *); 101192585Srmacklemstatic void nfs_sec_name(char *, int *); 102191783Srmacklemstatic void nfs_decode_args(struct mount *mp, struct nfsmount *nmp, 103214048Srmacklem struct nfs_args *argp, const char *, struct ucred *, 104214048Srmacklem struct thread *); 105191783Srmacklemstatic int mountnfs(struct nfs_args *, struct mount *, 106221014Srmacklem struct sockaddr *, char *, u_char *, int, u_char *, int, 107221014Srmacklem u_char *, int, struct vnode **, struct ucred *, 108221014Srmacklem struct thread *, int); 109214053Srmacklemstatic void nfs_getnlminfo(struct vnode *, uint8_t *, size_t *, 110216931Srmacklem struct sockaddr_storage *, int *, off_t *, 111216931Srmacklem struct timeval *); 112191783Srmacklemstatic vfs_mount_t nfs_mount; 113191783Srmacklemstatic vfs_cmount_t nfs_cmount; 114191783Srmacklemstatic vfs_unmount_t nfs_unmount; 115191783Srmacklemstatic vfs_root_t nfs_root; 116191783Srmacklemstatic vfs_statfs_t nfs_statfs; 117191783Srmacklemstatic vfs_sync_t nfs_sync; 118191783Srmacklemstatic vfs_sysctl_t nfs_sysctl; 119191783Srmacklem 120191783Srmacklem/* 121191783Srmacklem * nfs vfs operations. 122191783Srmacklem */ 123191783Srmacklemstatic struct vfsops nfs_vfsops = { 124191783Srmacklem .vfs_init = ncl_init, 125191783Srmacklem .vfs_mount = nfs_mount, 126191783Srmacklem .vfs_cmount = nfs_cmount, 127191783Srmacklem .vfs_root = nfs_root, 128191783Srmacklem .vfs_statfs = nfs_statfs, 129191783Srmacklem .vfs_sync = nfs_sync, 130191783Srmacklem .vfs_uninit = ncl_uninit, 131191783Srmacklem .vfs_unmount = nfs_unmount, 132191783Srmacklem .vfs_sysctl = nfs_sysctl, 133191783Srmacklem}; 134221124SrmacklemVFS_SET(nfs_vfsops, nfs, VFCF_NETWORK); 135191783Srmacklem 136191783Srmacklem/* So that loader and kldload(2) can find us, wherever we are.. */ 137221139SrmacklemMODULE_VERSION(nfs, 1); 138221139SrmacklemMODULE_DEPEND(nfs, nfscommon, 1, 1, 1); 139221139SrmacklemMODULE_DEPEND(nfs, krpc, 1, 1, 1); 140221139SrmacklemMODULE_DEPEND(nfs, nfssvc, 1, 1, 1); 141221139SrmacklemMODULE_DEPEND(nfs, nfslock, 1, 1, 1); 142191783Srmacklem 143191783Srmacklem/* 144221066Srmacklem * This structure is now defined in sys/nfs/nfs_diskless.c so that it 145221066Srmacklem * can be shared by both NFS clients. It is declared here so that it 146221066Srmacklem * will be defined for kernels built without NFS_ROOT, although it 147221066Srmacklem * isn't used in that case. 148191783Srmacklem */ 149221066Srmacklem#if !defined(NFS_ROOT) && !defined(NFSCLIENT) 150221066Srmacklemstruct nfs_diskless nfs_diskless = { { { 0 } } }; 151221066Srmacklemstruct nfsv3_diskless nfsv3_diskless = { { { 0 } } }; 152221066Srmacklemint nfs_diskless_valid = 0; 153221066Srmacklem#endif 154221066Srmacklem 155191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, diskless_valid, CTLFLAG_RD, 156221040Srmacklem &nfs_diskless_valid, 0, 157192145Srmacklem "Has the diskless struct been filled correctly"); 158191783Srmacklem 159191783SrmacklemSYSCTL_STRING(_vfs_newnfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD, 160221040Srmacklem nfsv3_diskless.root_hostnam, 0, "Path to nfs root"); 161191783Srmacklem 162191783SrmacklemSYSCTL_OPAQUE(_vfs_newnfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD, 163221040Srmacklem &nfsv3_diskless.root_saddr, sizeof(nfsv3_diskless.root_saddr), 164192145Srmacklem "%Ssockaddr_in", "Diskless root nfs address"); 165191783Srmacklem 166191783Srmacklem 167191783Srmacklemvoid newnfsargs_ntoh(struct nfs_args *); 168191783Srmacklemstatic int nfs_mountdiskless(char *, 169191783Srmacklem struct sockaddr_in *, struct nfs_args *, 170191783Srmacklem struct thread *, struct vnode **, struct mount *); 171191783Srmacklemstatic void nfs_convert_diskless(void); 172191783Srmacklemstatic void nfs_convert_oargs(struct nfs_args *args, 173191783Srmacklem struct onfs_args *oargs); 174191783Srmacklem 175191783Srmacklemint 176191783Srmacklemnewnfs_iosize(struct nfsmount *nmp) 177191783Srmacklem{ 178191783Srmacklem int iosize, maxio; 179191783Srmacklem 180191783Srmacklem /* First, set the upper limit for iosize */ 181191783Srmacklem if (nmp->nm_flag & NFSMNT_NFSV4) { 182191783Srmacklem maxio = NFS_MAXBSIZE; 183191783Srmacklem } else if (nmp->nm_flag & NFSMNT_NFSV3) { 184191783Srmacklem if (nmp->nm_sotype == SOCK_DGRAM) 185191783Srmacklem maxio = NFS_MAXDGRAMDATA; 186191783Srmacklem else 187191783Srmacklem maxio = NFS_MAXBSIZE; 188191783Srmacklem } else { 189191783Srmacklem maxio = NFS_V2MAXDATA; 190191783Srmacklem } 191191783Srmacklem if (nmp->nm_rsize > maxio || nmp->nm_rsize == 0) 192191783Srmacklem nmp->nm_rsize = maxio; 193191783Srmacklem if (nmp->nm_rsize > MAXBSIZE) 194191783Srmacklem nmp->nm_rsize = MAXBSIZE; 195191783Srmacklem if (nmp->nm_readdirsize > maxio || nmp->nm_readdirsize == 0) 196191783Srmacklem nmp->nm_readdirsize = maxio; 197191783Srmacklem if (nmp->nm_readdirsize > nmp->nm_rsize) 198191783Srmacklem nmp->nm_readdirsize = nmp->nm_rsize; 199191783Srmacklem if (nmp->nm_wsize > maxio || nmp->nm_wsize == 0) 200191783Srmacklem nmp->nm_wsize = maxio; 201191783Srmacklem if (nmp->nm_wsize > MAXBSIZE) 202191783Srmacklem nmp->nm_wsize = MAXBSIZE; 203191783Srmacklem 204191783Srmacklem /* 205191783Srmacklem * Calculate the size used for io buffers. Use the larger 206191783Srmacklem * of the two sizes to minimise nfs requests but make sure 207191783Srmacklem * that it is at least one VM page to avoid wasting buffer 208191783Srmacklem * space. 209191783Srmacklem */ 210191783Srmacklem iosize = imax(nmp->nm_rsize, nmp->nm_wsize); 211191783Srmacklem iosize = imax(iosize, PAGE_SIZE); 212191783Srmacklem nmp->nm_mountp->mnt_stat.f_iosize = iosize; 213191783Srmacklem return (iosize); 214191783Srmacklem} 215191783Srmacklem 216191783Srmacklemstatic void 217191783Srmacklemnfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs) 218191783Srmacklem{ 219191783Srmacklem 220191783Srmacklem args->version = NFS_ARGSVERSION; 221191783Srmacklem args->addr = oargs->addr; 222191783Srmacklem args->addrlen = oargs->addrlen; 223191783Srmacklem args->sotype = oargs->sotype; 224191783Srmacklem args->proto = oargs->proto; 225191783Srmacklem args->fh = oargs->fh; 226191783Srmacklem args->fhsize = oargs->fhsize; 227191783Srmacklem args->flags = oargs->flags; 228191783Srmacklem args->wsize = oargs->wsize; 229191783Srmacklem args->rsize = oargs->rsize; 230191783Srmacklem args->readdirsize = oargs->readdirsize; 231191783Srmacklem args->timeo = oargs->timeo; 232191783Srmacklem args->retrans = oargs->retrans; 233191783Srmacklem args->readahead = oargs->readahead; 234191783Srmacklem args->hostname = oargs->hostname; 235191783Srmacklem} 236191783Srmacklem 237191783Srmacklemstatic void 238191783Srmacklemnfs_convert_diskless(void) 239191783Srmacklem{ 240191783Srmacklem 241221040Srmacklem bcopy(&nfs_diskless.myif, &nfsv3_diskless.myif, 242221040Srmacklem sizeof(struct ifaliasreq)); 243221040Srmacklem bcopy(&nfs_diskless.mygateway, &nfsv3_diskless.mygateway, 244221040Srmacklem sizeof(struct sockaddr_in)); 245221040Srmacklem nfs_convert_oargs(&nfsv3_diskless.root_args,&nfs_diskless.root_args); 246221040Srmacklem if (nfsv3_diskless.root_args.flags & NFSMNT_NFSV3) { 247221040Srmacklem nfsv3_diskless.root_fhsize = NFSX_MYFH; 248221040Srmacklem bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_MYFH); 249191783Srmacklem } else { 250221040Srmacklem nfsv3_diskless.root_fhsize = NFSX_V2FH; 251221040Srmacklem bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_V2FH); 252191783Srmacklem } 253221040Srmacklem bcopy(&nfs_diskless.root_saddr,&nfsv3_diskless.root_saddr, 254221040Srmacklem sizeof(struct sockaddr_in)); 255221040Srmacklem bcopy(nfs_diskless.root_hostnam, nfsv3_diskless.root_hostnam, MNAMELEN); 256221040Srmacklem nfsv3_diskless.root_time = nfs_diskless.root_time; 257221040Srmacklem bcopy(nfs_diskless.my_hostnam, nfsv3_diskless.my_hostnam, 258221040Srmacklem MAXHOSTNAMELEN); 259221040Srmacklem nfs_diskless_valid = 3; 260191783Srmacklem} 261191783Srmacklem 262191783Srmacklem/* 263191783Srmacklem * nfs statfs call 264191783Srmacklem */ 265191783Srmacklemstatic int 266191990Sattilionfs_statfs(struct mount *mp, struct statfs *sbp) 267191783Srmacklem{ 268191783Srmacklem struct vnode *vp; 269191990Sattilio struct thread *td; 270191783Srmacklem struct nfsmount *nmp = VFSTONFS(mp); 271191783Srmacklem struct nfsvattr nfsva; 272191783Srmacklem struct nfsfsinfo fs; 273191783Srmacklem struct nfsstatfs sb; 274191783Srmacklem int error = 0, attrflag, gotfsinfo = 0, ret; 275191783Srmacklem struct nfsnode *np; 276191783Srmacklem 277191990Sattilio td = curthread; 278191990Sattilio 279191783Srmacklem error = vfs_busy(mp, MBF_NOWAIT); 280191783Srmacklem if (error) 281191783Srmacklem return (error); 282220732Srmacklem error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, LK_EXCLUSIVE); 283191783Srmacklem if (error) { 284191783Srmacklem vfs_unbusy(mp); 285191783Srmacklem return (error); 286191783Srmacklem } 287191783Srmacklem vp = NFSTOV(np); 288191783Srmacklem mtx_lock(&nmp->nm_mtx); 289191783Srmacklem if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) { 290191783Srmacklem mtx_unlock(&nmp->nm_mtx); 291191783Srmacklem error = nfsrpc_fsinfo(vp, &fs, td->td_ucred, td, &nfsva, 292191783Srmacklem &attrflag, NULL); 293191783Srmacklem if (!error) 294191783Srmacklem gotfsinfo = 1; 295191783Srmacklem } else 296191783Srmacklem mtx_unlock(&nmp->nm_mtx); 297191783Srmacklem if (!error) 298191783Srmacklem error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva, 299191783Srmacklem &attrflag, NULL); 300191783Srmacklem if (attrflag == 0) { 301191783Srmacklem ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1, 302191783Srmacklem td->td_ucred, td, &nfsva, NULL); 303191783Srmacklem if (ret) { 304191783Srmacklem /* 305191783Srmacklem * Just set default values to get things going. 306191783Srmacklem */ 307191783Srmacklem NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr)); 308191783Srmacklem nfsva.na_vattr.va_type = VDIR; 309191783Srmacklem nfsva.na_vattr.va_mode = 0777; 310191783Srmacklem nfsva.na_vattr.va_nlink = 100; 311191783Srmacklem nfsva.na_vattr.va_uid = (uid_t)0; 312191783Srmacklem nfsva.na_vattr.va_gid = (gid_t)0; 313191783Srmacklem nfsva.na_vattr.va_fileid = 2; 314191783Srmacklem nfsva.na_vattr.va_gen = 1; 315191783Srmacklem nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE; 316191783Srmacklem nfsva.na_vattr.va_size = 512 * 1024; 317191783Srmacklem } 318191783Srmacklem } 319191783Srmacklem (void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1); 320191783Srmacklem if (!error) { 321191783Srmacklem mtx_lock(&nmp->nm_mtx); 322191783Srmacklem if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4)) 323191783Srmacklem nfscl_loadfsinfo(nmp, &fs); 324191783Srmacklem nfscl_loadsbinfo(nmp, &sb, sbp); 325191783Srmacklem sbp->f_iosize = newnfs_iosize(nmp); 326191783Srmacklem mtx_unlock(&nmp->nm_mtx); 327191783Srmacklem if (sbp != &mp->mnt_stat) { 328191783Srmacklem bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); 329191783Srmacklem bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); 330191783Srmacklem } 331191783Srmacklem strncpy(&sbp->f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN); 332191783Srmacklem } else if (NFS_ISV4(vp)) { 333191783Srmacklem error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); 334191783Srmacklem } 335191783Srmacklem vput(vp); 336191783Srmacklem vfs_unbusy(mp); 337191783Srmacklem return (error); 338191783Srmacklem} 339191783Srmacklem 340191783Srmacklem/* 341191783Srmacklem * nfs version 3 fsinfo rpc call 342191783Srmacklem */ 343191783Srmacklemint 344191783Srmacklemncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred, 345191783Srmacklem struct thread *td) 346191783Srmacklem{ 347191783Srmacklem struct nfsfsinfo fs; 348191783Srmacklem struct nfsvattr nfsva; 349191783Srmacklem int error, attrflag; 350191783Srmacklem 351191783Srmacklem error = nfsrpc_fsinfo(vp, &fs, cred, td, &nfsva, &attrflag, NULL); 352191783Srmacklem if (!error) { 353191783Srmacklem if (attrflag) 354191783Srmacklem (void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 355191783Srmacklem 1); 356191783Srmacklem mtx_lock(&nmp->nm_mtx); 357191783Srmacklem nfscl_loadfsinfo(nmp, &fs); 358191783Srmacklem mtx_unlock(&nmp->nm_mtx); 359191783Srmacklem } 360191783Srmacklem return (error); 361191783Srmacklem} 362191783Srmacklem 363191783Srmacklem/* 364191783Srmacklem * Mount a remote root fs via. nfs. This depends on the info in the 365221040Srmacklem * nfs_diskless structure that has been filled in properly by some primary 366191783Srmacklem * bootstrap. 367191783Srmacklem * It goes something like this: 368191783Srmacklem * - do enough of "ifconfig" by calling ifioctl() so that the system 369191783Srmacklem * can talk to the server 370221040Srmacklem * - If nfs_diskless.mygateway is filled in, use that address as 371191783Srmacklem * a default gateway. 372191783Srmacklem * - build the rootfs mount point and call mountnfs() to do the rest. 373191783Srmacklem * 374191783Srmacklem * It is assumed to be safe to read, modify, and write the nfsv3_diskless 375191783Srmacklem * structure, as well as other global NFS client variables here, as 376192145Srmacklem * nfs_mountroot() will be called once in the boot before any other NFS 377191783Srmacklem * client activity occurs. 378191783Srmacklem */ 379221040Srmacklemstatic int 380221040Srmacklemnfs_mountroot(struct mount *mp) 381191783Srmacklem{ 382192145Srmacklem struct thread *td = curthread; 383221040Srmacklem struct nfsv3_diskless *nd = &nfsv3_diskless; 384191783Srmacklem struct socket *so; 385191783Srmacklem struct vnode *vp; 386191783Srmacklem struct ifreq ir; 387193066Sjamie int error; 388191783Srmacklem u_long l; 389191783Srmacklem char buf[128]; 390191783Srmacklem char *cp; 391191783Srmacklem 392191783Srmacklem#if defined(BOOTP_NFSROOT) && defined(BOOTP) 393192145Srmacklem bootpc_init(); /* use bootp to get nfs_diskless filled in */ 394191783Srmacklem#elif defined(NFS_ROOT) 395191783Srmacklem nfs_setup_diskless(); 396191783Srmacklem#endif 397191783Srmacklem 398221040Srmacklem if (nfs_diskless_valid == 0) 399191783Srmacklem return (-1); 400221040Srmacklem if (nfs_diskless_valid == 1) 401191783Srmacklem nfs_convert_diskless(); 402191783Srmacklem 403191783Srmacklem /* 404191783Srmacklem * XXX splnet, so networks will receive... 405191783Srmacklem */ 406191783Srmacklem splnet(); 407191783Srmacklem 408191783Srmacklem /* 409191783Srmacklem * Do enough of ifconfig(8) so that the critical net interface can 410191783Srmacklem * talk to the server. 411191783Srmacklem */ 412191783Srmacklem error = socreate(nd->myif.ifra_addr.sa_family, &so, nd->root_args.sotype, 0, 413191783Srmacklem td->td_ucred, td); 414191783Srmacklem if (error) 415192145Srmacklem panic("nfs_mountroot: socreate(%04x): %d", 416191783Srmacklem nd->myif.ifra_addr.sa_family, error); 417191783Srmacklem 418191783Srmacklem#if 0 /* XXX Bad idea */ 419191783Srmacklem /* 420191783Srmacklem * We might not have been told the right interface, so we pass 421191783Srmacklem * over the first ten interfaces of the same kind, until we get 422191783Srmacklem * one of them configured. 423191783Srmacklem */ 424191783Srmacklem 425191783Srmacklem for (i = strlen(nd->myif.ifra_name) - 1; 426191783Srmacklem nd->myif.ifra_name[i] >= '0' && 427191783Srmacklem nd->myif.ifra_name[i] <= '9'; 428191783Srmacklem nd->myif.ifra_name[i] ++) { 429191783Srmacklem error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td); 430191783Srmacklem if(!error) 431191783Srmacklem break; 432191783Srmacklem } 433191783Srmacklem#endif 434191783Srmacklem error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td); 435191783Srmacklem if (error) 436192145Srmacklem panic("nfs_mountroot: SIOCAIFADDR: %d", error); 437191783Srmacklem if ((cp = getenv("boot.netif.mtu")) != NULL) { 438191783Srmacklem ir.ifr_mtu = strtol(cp, NULL, 10); 439191783Srmacklem bcopy(nd->myif.ifra_name, ir.ifr_name, IFNAMSIZ); 440191783Srmacklem freeenv(cp); 441191783Srmacklem error = ifioctl(so, SIOCSIFMTU, (caddr_t)&ir, td); 442191783Srmacklem if (error) 443192145Srmacklem printf("nfs_mountroot: SIOCSIFMTU: %d", error); 444191783Srmacklem } 445191783Srmacklem soclose(so); 446191783Srmacklem 447191783Srmacklem /* 448191783Srmacklem * If the gateway field is filled in, set it as the default route. 449191783Srmacklem * Note that pxeboot will set a default route of 0 if the route 450191783Srmacklem * is not set by the DHCP server. Check also for a value of 0 451191783Srmacklem * to avoid panicking inappropriately in that situation. 452191783Srmacklem */ 453191783Srmacklem if (nd->mygateway.sin_len != 0 && 454191783Srmacklem nd->mygateway.sin_addr.s_addr != 0) { 455191783Srmacklem struct sockaddr_in mask, sin; 456191783Srmacklem 457191783Srmacklem bzero((caddr_t)&mask, sizeof(mask)); 458191783Srmacklem sin = mask; 459191783Srmacklem sin.sin_family = AF_INET; 460191783Srmacklem sin.sin_len = sizeof(sin); 461192145Srmacklem /* XXX MRT use table 0 for this sort of thing */ 462218757Sbz CURVNET_SET(TD_TO_VNET(td)); 463191783Srmacklem error = rtrequest(RTM_ADD, (struct sockaddr *)&sin, 464191783Srmacklem (struct sockaddr *)&nd->mygateway, 465191783Srmacklem (struct sockaddr *)&mask, 466191783Srmacklem RTF_UP | RTF_GATEWAY, NULL); 467218757Sbz CURVNET_RESTORE(); 468191783Srmacklem if (error) 469192145Srmacklem panic("nfs_mountroot: RTM_ADD: %d", error); 470191783Srmacklem } 471191783Srmacklem 472191783Srmacklem /* 473191783Srmacklem * Create the rootfs mount point. 474191783Srmacklem */ 475191783Srmacklem nd->root_args.fh = nd->root_fh; 476191783Srmacklem nd->root_args.fhsize = nd->root_fhsize; 477191783Srmacklem l = ntohl(nd->root_saddr.sin_addr.s_addr); 478191783Srmacklem snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld:%s", 479191783Srmacklem (l >> 24) & 0xff, (l >> 16) & 0xff, 480191783Srmacklem (l >> 8) & 0xff, (l >> 0) & 0xff, nd->root_hostnam); 481191783Srmacklem printf("NFS ROOT: %s\n", buf); 482192145Srmacklem nd->root_args.hostname = buf; 483191783Srmacklem if ((error = nfs_mountdiskless(buf, 484191783Srmacklem &nd->root_saddr, &nd->root_args, td, &vp, mp)) != 0) { 485191783Srmacklem return (error); 486191783Srmacklem } 487191783Srmacklem 488191783Srmacklem /* 489191783Srmacklem * This is not really an nfs issue, but it is much easier to 490191783Srmacklem * set hostname here and then let the "/etc/rc.xxx" files 491191783Srmacklem * mount the right /var based upon its preset value. 492191783Srmacklem */ 493193066Sjamie mtx_lock(&prison0.pr_mtx); 494194118Sjamie strlcpy(prison0.pr_hostname, nd->my_hostnam, 495194118Sjamie sizeof(prison0.pr_hostname)); 496193066Sjamie mtx_unlock(&prison0.pr_mtx); 497191783Srmacklem inittodr(ntohl(nd->root_time)); 498191783Srmacklem return (0); 499191783Srmacklem} 500191783Srmacklem 501191783Srmacklem/* 502191783Srmacklem * Internal version of mount system call for diskless setup. 503191783Srmacklem */ 504191783Srmacklemstatic int 505191783Srmacklemnfs_mountdiskless(char *path, 506191783Srmacklem struct sockaddr_in *sin, struct nfs_args *args, struct thread *td, 507191783Srmacklem struct vnode **vpp, struct mount *mp) 508191783Srmacklem{ 509191783Srmacklem struct sockaddr *nam; 510221014Srmacklem int dirlen, error; 511221014Srmacklem char *dirpath; 512191783Srmacklem 513221014Srmacklem /* 514221014Srmacklem * Find the directory path in "path", which also has the server's 515221014Srmacklem * name/ip address in it. 516221014Srmacklem */ 517221014Srmacklem dirpath = strchr(path, ':'); 518221014Srmacklem if (dirpath != NULL) 519221014Srmacklem dirlen = strlen(++dirpath); 520221014Srmacklem else 521221014Srmacklem dirlen = 0; 522191783Srmacklem nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK); 523221014Srmacklem if ((error = mountnfs(args, mp, nam, path, NULL, 0, dirpath, dirlen, 524221014Srmacklem NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NEGNAMETIMEO)) != 0) { 525192145Srmacklem printf("nfs_mountroot: mount %s on /: %d\n", path, error); 526191783Srmacklem return (error); 527191783Srmacklem } 528191783Srmacklem return (0); 529191783Srmacklem} 530191783Srmacklem 531191783Srmacklemstatic void 532192585Srmacklemnfs_sec_name(char *sec, int *flagsp) 533192585Srmacklem{ 534192585Srmacklem if (!strcmp(sec, "krb5")) 535192585Srmacklem *flagsp |= NFSMNT_KERB; 536192585Srmacklem else if (!strcmp(sec, "krb5i")) 537192585Srmacklem *flagsp |= (NFSMNT_KERB | NFSMNT_INTEGRITY); 538192585Srmacklem else if (!strcmp(sec, "krb5p")) 539192585Srmacklem *flagsp |= (NFSMNT_KERB | NFSMNT_PRIVACY); 540192585Srmacklem} 541192585Srmacklem 542192585Srmacklemstatic void 543191783Srmacklemnfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp, 544214048Srmacklem const char *hostname, struct ucred *cred, struct thread *td) 545191783Srmacklem{ 546191783Srmacklem int s; 547191783Srmacklem int adjsock; 548214048Srmacklem char *p; 549191783Srmacklem 550191783Srmacklem s = splnet(); 551191783Srmacklem 552191783Srmacklem /* 553191783Srmacklem * Set read-only flag if requested; otherwise, clear it if this is 554191783Srmacklem * an update. If this is not an update, then either the read-only 555191783Srmacklem * flag is already clear, or this is a root mount and it was set 556191783Srmacklem * intentionally at some previous point. 557191783Srmacklem */ 558191783Srmacklem if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) { 559191783Srmacklem MNT_ILOCK(mp); 560191783Srmacklem mp->mnt_flag |= MNT_RDONLY; 561191783Srmacklem MNT_IUNLOCK(mp); 562191783Srmacklem } else if (mp->mnt_flag & MNT_UPDATE) { 563191783Srmacklem MNT_ILOCK(mp); 564191783Srmacklem mp->mnt_flag &= ~MNT_RDONLY; 565191783Srmacklem MNT_IUNLOCK(mp); 566191783Srmacklem } 567191783Srmacklem 568191783Srmacklem /* 569191783Srmacklem * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes 570191783Srmacklem * no sense in that context. Also, set up appropriate retransmit 571191783Srmacklem * and soft timeout behavior. 572191783Srmacklem */ 573191783Srmacklem if (argp->sotype == SOCK_STREAM) { 574191783Srmacklem nmp->nm_flag &= ~NFSMNT_NOCONN; 575191783Srmacklem nmp->nm_timeo = NFS_MAXTIMEO; 576220739Srmacklem if ((argp->flags & NFSMNT_NFSV4) != 0) 577220739Srmacklem nmp->nm_retry = INT_MAX; 578220739Srmacklem else 579220739Srmacklem nmp->nm_retry = NFS_RETRANS_TCP; 580191783Srmacklem } 581191783Srmacklem 582220739Srmacklem /* Also clear RDIRPLUS if NFSv2, it crashes some servers */ 583220739Srmacklem if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) { 584220739Srmacklem argp->flags &= ~NFSMNT_RDIRPLUS; 585191783Srmacklem nmp->nm_flag &= ~NFSMNT_RDIRPLUS; 586220739Srmacklem } 587191783Srmacklem 588220739Srmacklem /* Clear NFSMNT_RESVPORT for NFSv4, since it is not required. */ 589220739Srmacklem if ((argp->flags & NFSMNT_NFSV4) != 0) { 590220739Srmacklem argp->flags &= ~NFSMNT_RESVPORT; 591220739Srmacklem nmp->nm_flag &= ~NFSMNT_RESVPORT; 592220739Srmacklem } 593220739Srmacklem 594220739Srmacklem /* Re-bind if rsrvd port requested and wasn't on one */ 595220739Srmacklem adjsock = !(nmp->nm_flag & NFSMNT_RESVPORT) 596220739Srmacklem && (argp->flags & NFSMNT_RESVPORT); 597191783Srmacklem /* Also re-bind if we're switching to/from a connected UDP socket */ 598220739Srmacklem adjsock |= ((nmp->nm_flag & NFSMNT_NOCONN) != 599191783Srmacklem (argp->flags & NFSMNT_NOCONN)); 600191783Srmacklem 601191783Srmacklem /* Update flags atomically. Don't change the lock bits. */ 602191783Srmacklem nmp->nm_flag = argp->flags | nmp->nm_flag; 603191783Srmacklem splx(s); 604191783Srmacklem 605191783Srmacklem if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) { 606191783Srmacklem nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10; 607191783Srmacklem if (nmp->nm_timeo < NFS_MINTIMEO) 608191783Srmacklem nmp->nm_timeo = NFS_MINTIMEO; 609191783Srmacklem else if (nmp->nm_timeo > NFS_MAXTIMEO) 610191783Srmacklem nmp->nm_timeo = NFS_MAXTIMEO; 611191783Srmacklem } 612191783Srmacklem 613191783Srmacklem if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) { 614191783Srmacklem nmp->nm_retry = argp->retrans; 615191783Srmacklem if (nmp->nm_retry > NFS_MAXREXMIT) 616191783Srmacklem nmp->nm_retry = NFS_MAXREXMIT; 617191783Srmacklem } 618191783Srmacklem 619191783Srmacklem if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) { 620191783Srmacklem nmp->nm_wsize = argp->wsize; 621191783Srmacklem /* Round down to multiple of blocksize */ 622191783Srmacklem nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1); 623191783Srmacklem if (nmp->nm_wsize <= 0) 624191783Srmacklem nmp->nm_wsize = NFS_FABLKSIZE; 625191783Srmacklem } 626191783Srmacklem 627191783Srmacklem if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) { 628191783Srmacklem nmp->nm_rsize = argp->rsize; 629191783Srmacklem /* Round down to multiple of blocksize */ 630191783Srmacklem nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1); 631191783Srmacklem if (nmp->nm_rsize <= 0) 632191783Srmacklem nmp->nm_rsize = NFS_FABLKSIZE; 633191783Srmacklem } 634191783Srmacklem 635191783Srmacklem if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) { 636191783Srmacklem nmp->nm_readdirsize = argp->readdirsize; 637191783Srmacklem } 638191783Srmacklem 639191783Srmacklem if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0) 640191783Srmacklem nmp->nm_acregmin = argp->acregmin; 641191783Srmacklem else 642191783Srmacklem nmp->nm_acregmin = NFS_MINATTRTIMO; 643191783Srmacklem if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0) 644191783Srmacklem nmp->nm_acregmax = argp->acregmax; 645191783Srmacklem else 646191783Srmacklem nmp->nm_acregmax = NFS_MAXATTRTIMO; 647191783Srmacklem if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0) 648191783Srmacklem nmp->nm_acdirmin = argp->acdirmin; 649191783Srmacklem else 650191783Srmacklem nmp->nm_acdirmin = NFS_MINDIRATTRTIMO; 651191783Srmacklem if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0) 652191783Srmacklem nmp->nm_acdirmax = argp->acdirmax; 653191783Srmacklem else 654191783Srmacklem nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO; 655191783Srmacklem if (nmp->nm_acdirmin > nmp->nm_acdirmax) 656191783Srmacklem nmp->nm_acdirmin = nmp->nm_acdirmax; 657191783Srmacklem if (nmp->nm_acregmin > nmp->nm_acregmax) 658191783Srmacklem nmp->nm_acregmin = nmp->nm_acregmax; 659191783Srmacklem 660191783Srmacklem if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) { 661191783Srmacklem if (argp->readahead <= NFS_MAXRAHEAD) 662191783Srmacklem nmp->nm_readahead = argp->readahead; 663191783Srmacklem else 664191783Srmacklem nmp->nm_readahead = NFS_MAXRAHEAD; 665191783Srmacklem } 666191783Srmacklem if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) { 667191783Srmacklem if (argp->wcommitsize < nmp->nm_wsize) 668191783Srmacklem nmp->nm_wcommitsize = nmp->nm_wsize; 669191783Srmacklem else 670191783Srmacklem nmp->nm_wcommitsize = argp->wcommitsize; 671191783Srmacklem } 672191783Srmacklem 673191783Srmacklem adjsock |= ((nmp->nm_sotype != argp->sotype) || 674191783Srmacklem (nmp->nm_soproto != argp->proto)); 675191783Srmacklem 676191783Srmacklem if (nmp->nm_client != NULL && adjsock) { 677191783Srmacklem int haslock = 0, error = 0; 678191783Srmacklem 679191783Srmacklem if (nmp->nm_sotype == SOCK_STREAM) { 680191783Srmacklem error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock); 681191783Srmacklem if (!error) 682191783Srmacklem haslock = 1; 683191783Srmacklem } 684191783Srmacklem if (!error) { 685191783Srmacklem newnfs_disconnect(&nmp->nm_sockreq); 686191783Srmacklem if (haslock) 687191783Srmacklem newnfs_sndunlock(&nmp->nm_sockreq.nr_lock); 688191783Srmacklem nmp->nm_sotype = argp->sotype; 689191783Srmacklem nmp->nm_soproto = argp->proto; 690191783Srmacklem if (nmp->nm_sotype == SOCK_DGRAM) 691191783Srmacklem while (newnfs_connect(nmp, &nmp->nm_sockreq, 692191783Srmacklem cred, td, 0)) { 693191783Srmacklem printf("newnfs_args: retrying connect\n"); 694207170Srmacklem (void) nfs_catnap(PSOCK, 0, "newnfscon"); 695191783Srmacklem } 696191783Srmacklem } 697191783Srmacklem } else { 698191783Srmacklem nmp->nm_sotype = argp->sotype; 699191783Srmacklem nmp->nm_soproto = argp->proto; 700191783Srmacklem } 701214048Srmacklem 702214048Srmacklem if (hostname != NULL) { 703214048Srmacklem strlcpy(nmp->nm_hostname, hostname, 704214048Srmacklem sizeof(nmp->nm_hostname)); 705214048Srmacklem p = strchr(nmp->nm_hostname, ':'); 706214048Srmacklem if (p != NULL) 707214048Srmacklem *p = '\0'; 708214048Srmacklem } 709191783Srmacklem} 710191783Srmacklem 711221190Srmacklemstatic const char *nfs_opts[] = { "from", "nfs_args", 712191783Srmacklem "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union", 713191783Srmacklem "noclusterr", "noclusterw", "multilabel", "acls", "force", "update", 714192585Srmacklem "async", "noconn", "nolockd", "conn", "lockd", "intr", "rdirplus", 715192585Srmacklem "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize", 716192585Srmacklem "retrans", "acregmin", "acregmax", "acdirmin", "acdirmax", "resvport", 717192585Srmacklem "readahead", "hostname", "timeout", "addr", "fh", "nfsv3", "sec", 718192585Srmacklem "principal", "nfsv4", "gssname", "allgssname", "dirpath", 719221436Sru "negnametimeo", "nocto", 720191783Srmacklem NULL }; 721191783Srmacklem 722191783Srmacklem/* 723191783Srmacklem * VFS Operations. 724191783Srmacklem * 725191783Srmacklem * mount system call 726191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then 727191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before 728191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and 729191783Srmacklem * an error after that means that I have to release the mbuf. 730191783Srmacklem */ 731191783Srmacklem/* ARGSUSED */ 732191783Srmacklemstatic int 733191990Sattilionfs_mount(struct mount *mp) 734191783Srmacklem{ 735191783Srmacklem struct nfs_args args = { 736191783Srmacklem .version = NFS_ARGSVERSION, 737191783Srmacklem .addr = NULL, 738191783Srmacklem .addrlen = sizeof (struct sockaddr_in), 739191783Srmacklem .sotype = SOCK_STREAM, 740191783Srmacklem .proto = 0, 741191783Srmacklem .fh = NULL, 742191783Srmacklem .fhsize = 0, 743220739Srmacklem .flags = NFSMNT_RESVPORT, 744191783Srmacklem .wsize = NFS_WSIZE, 745191783Srmacklem .rsize = NFS_RSIZE, 746191783Srmacklem .readdirsize = NFS_READDIRSIZE, 747191783Srmacklem .timeo = 10, 748191783Srmacklem .retrans = NFS_RETRANS, 749191783Srmacklem .readahead = NFS_DEFRAHEAD, 750191783Srmacklem .wcommitsize = 0, /* was: NQ_DEFLEASE */ 751191783Srmacklem .hostname = NULL, 752191783Srmacklem .acregmin = NFS_MINATTRTIMO, 753191783Srmacklem .acregmax = NFS_MAXATTRTIMO, 754191783Srmacklem .acdirmin = NFS_MINDIRATTRTIMO, 755191783Srmacklem .acdirmax = NFS_MAXDIRATTRTIMO, 756191783Srmacklem }; 757192585Srmacklem int error = 0, ret, len; 758192585Srmacklem struct sockaddr *nam = NULL; 759191783Srmacklem struct vnode *vp; 760191990Sattilio struct thread *td; 761191783Srmacklem char hst[MNAMELEN]; 762191783Srmacklem u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100]; 763192585Srmacklem char *opt, *name, *secname; 764203303Srmacklem int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO; 765221190Srmacklem int dirlen, has_nfs_args_opt, krbnamelen, srvkrbnamelen; 766221205Srmacklem size_t hstlen; 767191783Srmacklem 768221190Srmacklem has_nfs_args_opt = 0; 769191783Srmacklem if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) { 770191783Srmacklem error = EINVAL; 771191783Srmacklem goto out; 772191783Srmacklem } 773191783Srmacklem 774191990Sattilio td = curthread; 775191783Srmacklem if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS) { 776221040Srmacklem error = nfs_mountroot(mp); 777191783Srmacklem goto out; 778191783Srmacklem } 779191783Srmacklem 780192585Srmacklem nfscl_init(); 781191783Srmacklem 782221190Srmacklem /* 783221190Srmacklem * The old mount_nfs program passed the struct nfs_args 784221190Srmacklem * from userspace to kernel. The new mount_nfs program 785221190Srmacklem * passes string options via nmount() from userspace to kernel 786221190Srmacklem * and we populate the struct nfs_args in the kernel. 787221190Srmacklem */ 788221190Srmacklem if (vfs_getopt(mp->mnt_optnew, "nfs_args", NULL, NULL) == 0) { 789221190Srmacklem error = vfs_copyopt(mp->mnt_optnew, "nfs_args", &args, 790221190Srmacklem sizeof(args)); 791221190Srmacklem if (error != 0) 792221190Srmacklem goto out; 793221190Srmacklem 794221190Srmacklem if (args.version != NFS_ARGSVERSION) { 795221190Srmacklem error = EPROGMISMATCH; 796221190Srmacklem goto out; 797221190Srmacklem } 798221190Srmacklem has_nfs_args_opt = 1; 799221190Srmacklem } 800221190Srmacklem 801192585Srmacklem /* Handle the new style options. */ 802192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0) 803192585Srmacklem args.flags |= NFSMNT_NOCONN; 804192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0) 805192585Srmacklem args.flags |= NFSMNT_NOCONN; 806192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0) 807192585Srmacklem args.flags |= NFSMNT_NOLOCKD; 808192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0) 809192585Srmacklem args.flags &= ~NFSMNT_NOLOCKD; 810192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0) 811192585Srmacklem args.flags |= NFSMNT_INT; 812192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0) 813192585Srmacklem args.flags |= NFSMNT_RDIRPLUS; 814192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0) 815192585Srmacklem args.flags |= NFSMNT_RESVPORT; 816192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0) 817192585Srmacklem args.flags &= ~NFSMNT_RESVPORT; 818192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0) 819192585Srmacklem args.flags |= NFSMNT_SOFT; 820192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0) 821192585Srmacklem args.flags &= ~NFSMNT_SOFT; 822192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0) 823192585Srmacklem args.sotype = SOCK_DGRAM; 824192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0) 825192585Srmacklem args.sotype = SOCK_DGRAM; 826192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0) 827192585Srmacklem args.sotype = SOCK_STREAM; 828192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0) 829192585Srmacklem args.flags |= NFSMNT_NFSV3; 830192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "nfsv4", NULL, NULL) == 0) { 831192585Srmacklem args.flags |= NFSMNT_NFSV4; 832192585Srmacklem args.sotype = SOCK_STREAM; 833191783Srmacklem } 834192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "allgssname", NULL, NULL) == 0) 835192585Srmacklem args.flags |= NFSMNT_ALLGSSNAME; 836221436Sru if (vfs_getopt(mp->mnt_optnew, "nocto", NULL, NULL) == 0) 837221436Sru args.flags |= NFSMNT_NOCTO; 838192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) { 839192585Srmacklem if (opt == NULL) { 840192585Srmacklem vfs_mount_error(mp, "illegal readdirsize"); 841192585Srmacklem error = EINVAL; 842192585Srmacklem goto out; 843192585Srmacklem } 844192585Srmacklem ret = sscanf(opt, "%d", &args.readdirsize); 845192585Srmacklem if (ret != 1 || args.readdirsize <= 0) { 846192585Srmacklem vfs_mount_error(mp, "illegal readdirsize: %s", 847192585Srmacklem opt); 848192585Srmacklem error = EINVAL; 849192585Srmacklem goto out; 850192585Srmacklem } 851192585Srmacklem args.flags |= NFSMNT_READDIRSIZE; 852192585Srmacklem } 853192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) { 854192585Srmacklem if (opt == NULL) { 855192585Srmacklem vfs_mount_error(mp, "illegal readahead"); 856192585Srmacklem error = EINVAL; 857192585Srmacklem goto out; 858192585Srmacklem } 859192585Srmacklem ret = sscanf(opt, "%d", &args.readahead); 860192585Srmacklem if (ret != 1 || args.readahead <= 0) { 861192585Srmacklem vfs_mount_error(mp, "illegal readahead: %s", 862192585Srmacklem opt); 863192585Srmacklem error = EINVAL; 864192585Srmacklem goto out; 865192585Srmacklem } 866192585Srmacklem args.flags |= NFSMNT_READAHEAD; 867192585Srmacklem } 868192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) { 869192585Srmacklem if (opt == NULL) { 870192585Srmacklem vfs_mount_error(mp, "illegal wsize"); 871192585Srmacklem error = EINVAL; 872192585Srmacklem goto out; 873192585Srmacklem } 874192585Srmacklem ret = sscanf(opt, "%d", &args.wsize); 875192585Srmacklem if (ret != 1 || args.wsize <= 0) { 876192585Srmacklem vfs_mount_error(mp, "illegal wsize: %s", 877192585Srmacklem opt); 878192585Srmacklem error = EINVAL; 879192585Srmacklem goto out; 880192585Srmacklem } 881192585Srmacklem args.flags |= NFSMNT_WSIZE; 882192585Srmacklem } 883192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) { 884192585Srmacklem if (opt == NULL) { 885192585Srmacklem vfs_mount_error(mp, "illegal rsize"); 886192585Srmacklem error = EINVAL; 887192585Srmacklem goto out; 888192585Srmacklem } 889192585Srmacklem ret = sscanf(opt, "%d", &args.rsize); 890192585Srmacklem if (ret != 1 || args.rsize <= 0) { 891192585Srmacklem vfs_mount_error(mp, "illegal wsize: %s", 892192585Srmacklem opt); 893192585Srmacklem error = EINVAL; 894192585Srmacklem goto out; 895192585Srmacklem } 896192585Srmacklem args.flags |= NFSMNT_RSIZE; 897192585Srmacklem } 898192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) { 899192585Srmacklem if (opt == NULL) { 900192585Srmacklem vfs_mount_error(mp, "illegal retrans"); 901192585Srmacklem error = EINVAL; 902192585Srmacklem goto out; 903192585Srmacklem } 904192585Srmacklem ret = sscanf(opt, "%d", &args.retrans); 905192585Srmacklem if (ret != 1 || args.retrans <= 0) { 906192585Srmacklem vfs_mount_error(mp, "illegal retrans: %s", 907192585Srmacklem opt); 908192585Srmacklem error = EINVAL; 909192585Srmacklem goto out; 910192585Srmacklem } 911192585Srmacklem args.flags |= NFSMNT_RETRANS; 912192585Srmacklem } 913192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) { 914192585Srmacklem ret = sscanf(opt, "%d", &args.acregmin); 915192585Srmacklem if (ret != 1 || args.acregmin < 0) { 916192585Srmacklem vfs_mount_error(mp, "illegal acregmin: %s", 917192585Srmacklem opt); 918192585Srmacklem error = EINVAL; 919192585Srmacklem goto out; 920192585Srmacklem } 921192585Srmacklem args.flags |= NFSMNT_ACREGMIN; 922192585Srmacklem } 923192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) { 924192585Srmacklem ret = sscanf(opt, "%d", &args.acregmax); 925192585Srmacklem if (ret != 1 || args.acregmax < 0) { 926192585Srmacklem vfs_mount_error(mp, "illegal acregmax: %s", 927192585Srmacklem opt); 928192585Srmacklem error = EINVAL; 929192585Srmacklem goto out; 930192585Srmacklem } 931192585Srmacklem args.flags |= NFSMNT_ACREGMAX; 932192585Srmacklem } 933192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) { 934192585Srmacklem ret = sscanf(opt, "%d", &args.acdirmin); 935192585Srmacklem if (ret != 1 || args.acdirmin < 0) { 936192585Srmacklem vfs_mount_error(mp, "illegal acdirmin: %s", 937192585Srmacklem opt); 938192585Srmacklem error = EINVAL; 939192585Srmacklem goto out; 940192585Srmacklem } 941192585Srmacklem args.flags |= NFSMNT_ACDIRMIN; 942192585Srmacklem } 943192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) { 944192585Srmacklem ret = sscanf(opt, "%d", &args.acdirmax); 945192585Srmacklem if (ret != 1 || args.acdirmax < 0) { 946192585Srmacklem vfs_mount_error(mp, "illegal acdirmax: %s", 947192585Srmacklem opt); 948192585Srmacklem error = EINVAL; 949192585Srmacklem goto out; 950192585Srmacklem } 951192585Srmacklem args.flags |= NFSMNT_ACDIRMAX; 952192585Srmacklem } 953192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) { 954192585Srmacklem ret = sscanf(opt, "%d", &args.timeo); 955192585Srmacklem if (ret != 1 || args.timeo <= 0) { 956192585Srmacklem vfs_mount_error(mp, "illegal timeout: %s", 957192585Srmacklem opt); 958192585Srmacklem error = EINVAL; 959192585Srmacklem goto out; 960192585Srmacklem } 961192585Srmacklem args.flags |= NFSMNT_TIMEO; 962192585Srmacklem } 963203303Srmacklem if (vfs_getopt(mp->mnt_optnew, "negnametimeo", (void **)&opt, NULL) 964203303Srmacklem == 0) { 965203303Srmacklem ret = sscanf(opt, "%d", &negnametimeo); 966203303Srmacklem if (ret != 1 || negnametimeo < 0) { 967203303Srmacklem vfs_mount_error(mp, "illegal negnametimeo: %s", 968203303Srmacklem opt); 969203303Srmacklem error = EINVAL; 970203303Srmacklem goto out; 971203303Srmacklem } 972203303Srmacklem } 973192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "sec", 974192585Srmacklem (void **) &secname, NULL) == 0) 975192585Srmacklem nfs_sec_name(secname, &args.flags); 976191783Srmacklem 977191783Srmacklem if (mp->mnt_flag & MNT_UPDATE) { 978191783Srmacklem struct nfsmount *nmp = VFSTONFS(mp); 979191783Srmacklem 980191783Srmacklem if (nmp == NULL) { 981191783Srmacklem error = EIO; 982191783Srmacklem goto out; 983191783Srmacklem } 984191783Srmacklem /* 985191783Srmacklem * When doing an update, we can't change version, 986191783Srmacklem * security, switch lockd strategies or change cookie 987191783Srmacklem * translation 988191783Srmacklem */ 989191783Srmacklem args.flags = (args.flags & 990191783Srmacklem ~(NFSMNT_NFSV3 | 991191783Srmacklem NFSMNT_NFSV4 | 992191783Srmacklem NFSMNT_KERB | 993191783Srmacklem NFSMNT_INTEGRITY | 994191783Srmacklem NFSMNT_PRIVACY | 995191783Srmacklem NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) | 996191783Srmacklem (nmp->nm_flag & 997191783Srmacklem (NFSMNT_NFSV3 | 998191783Srmacklem NFSMNT_NFSV4 | 999191783Srmacklem NFSMNT_KERB | 1000191783Srmacklem NFSMNT_INTEGRITY | 1001191783Srmacklem NFSMNT_PRIVACY | 1002191783Srmacklem NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)); 1003214048Srmacklem nfs_decode_args(mp, nmp, &args, NULL, td->td_ucred, td); 1004191783Srmacklem goto out; 1005191783Srmacklem } 1006191783Srmacklem 1007191783Srmacklem /* 1008191783Srmacklem * Make the nfs_ip_paranoia sysctl serve as the default connection 1009191783Srmacklem * or no-connection mode for those protocols that support 1010191783Srmacklem * no-connection mode (the flag will be cleared later for protocols 1011191783Srmacklem * that do not support no-connection mode). This will allow a client 1012191783Srmacklem * to receive replies from a different IP then the request was 1013191783Srmacklem * sent to. Note: default value for nfs_ip_paranoia is 1 (paranoid), 1014191783Srmacklem * not 0. 1015191783Srmacklem */ 1016191783Srmacklem if (nfs_ip_paranoia == 0) 1017191783Srmacklem args.flags |= NFSMNT_NOCONN; 1018192585Srmacklem 1019221190Srmacklem if (has_nfs_args_opt != 0) { 1020221190Srmacklem /* 1021221190Srmacklem * In the 'nfs_args' case, the pointers in the args 1022221190Srmacklem * structure are in userland - we copy them in here. 1023221190Srmacklem */ 1024221190Srmacklem if (args.fhsize < 0 || args.fhsize > NFSX_V3FHMAX) { 1025192585Srmacklem vfs_mount_error(mp, "Bad file handle"); 1026191783Srmacklem error = EINVAL; 1027191783Srmacklem goto out; 1028191783Srmacklem } 1029221190Srmacklem error = copyin((caddr_t)args.fh, (caddr_t)nfh, 1030221190Srmacklem args.fhsize); 1031221190Srmacklem if (error != 0) 1032221190Srmacklem goto out; 1033221205Srmacklem error = copyinstr(args.hostname, hst, MNAMELEN - 1, &hstlen); 1034221190Srmacklem if (error != 0) 1035221190Srmacklem goto out; 1036221205Srmacklem bzero(&hst[hstlen], MNAMELEN - hstlen); 1037221190Srmacklem args.hostname = hst; 1038221190Srmacklem /* sockargs() call must be after above copyin() calls */ 1039221190Srmacklem error = getsockaddr(&nam, (caddr_t)args.addr, 1040221190Srmacklem args.addrlen); 1041221190Srmacklem if (error != 0) 1042221190Srmacklem goto out; 1043191783Srmacklem } else { 1044221190Srmacklem if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh, 1045221190Srmacklem &args.fhsize) == 0) { 1046221190Srmacklem if (args.fhsize < 0 || args.fhsize > NFSX_FHMAX) { 1047221190Srmacklem vfs_mount_error(mp, "Bad file handle"); 1048221190Srmacklem error = EINVAL; 1049221190Srmacklem goto out; 1050221190Srmacklem } 1051221190Srmacklem bcopy(args.fh, nfh, args.fhsize); 1052221190Srmacklem } else { 1053221190Srmacklem args.fhsize = 0; 1054221190Srmacklem } 1055221190Srmacklem (void) vfs_getopt(mp->mnt_optnew, "hostname", 1056221190Srmacklem (void **)&args.hostname, &len); 1057221190Srmacklem if (args.hostname == NULL) { 1058221190Srmacklem vfs_mount_error(mp, "Invalid hostname"); 1059221190Srmacklem error = EINVAL; 1060221190Srmacklem goto out; 1061221190Srmacklem } 1062221190Srmacklem bcopy(args.hostname, hst, MNAMELEN); 1063221190Srmacklem hst[MNAMELEN - 1] = '\0'; 1064192585Srmacklem } 1065192585Srmacklem 1066192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "principal", (void **)&name, NULL) == 0) 1067192585Srmacklem strlcpy(srvkrbname, name, sizeof (srvkrbname)); 1068192585Srmacklem else 1069192585Srmacklem snprintf(srvkrbname, sizeof (srvkrbname), "nfs@%s", hst); 1070221014Srmacklem srvkrbnamelen = strlen(srvkrbname); 1071192585Srmacklem 1072192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "gssname", (void **)&name, NULL) == 0) 1073192585Srmacklem strlcpy(krbname, name, sizeof (krbname)); 1074192585Srmacklem else 1075191783Srmacklem krbname[0] = '\0'; 1076221014Srmacklem krbnamelen = strlen(krbname); 1077192585Srmacklem 1078192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "dirpath", (void **)&name, NULL) == 0) 1079192585Srmacklem strlcpy(dirpath, name, sizeof (dirpath)); 1080192585Srmacklem else 1081191783Srmacklem dirpath[0] = '\0'; 1082221014Srmacklem dirlen = strlen(dirpath); 1083192585Srmacklem 1084221190Srmacklem if (has_nfs_args_opt == 0 && vfs_getopt(mp->mnt_optnew, "addr", 1085221190Srmacklem (void **)&args.addr, &args.addrlen) == 0) { 1086192585Srmacklem if (args.addrlen > SOCK_MAXADDRLEN) { 1087192585Srmacklem error = ENAMETOOLONG; 1088191783Srmacklem goto out; 1089191783Srmacklem } 1090192585Srmacklem nam = malloc(args.addrlen, M_SONAME, M_WAITOK); 1091192585Srmacklem bcopy(args.addr, nam, args.addrlen); 1092192585Srmacklem nam->sa_len = args.addrlen; 1093191783Srmacklem } 1094192585Srmacklem 1095191783Srmacklem args.fh = nfh; 1096221014Srmacklem error = mountnfs(&args, mp, nam, hst, krbname, krbnamelen, dirpath, 1097221014Srmacklem dirlen, srvkrbname, srvkrbnamelen, &vp, td->td_ucred, td, 1098221014Srmacklem negnametimeo); 1099191783Srmacklemout: 1100191783Srmacklem if (!error) { 1101191783Srmacklem MNT_ILOCK(mp); 1102191783Srmacklem mp->mnt_kern_flag |= (MNTK_MPSAFE|MNTK_LOOKUP_SHARED); 1103191783Srmacklem MNT_IUNLOCK(mp); 1104191783Srmacklem } 1105191783Srmacklem return (error); 1106191783Srmacklem} 1107191783Srmacklem 1108191783Srmacklem 1109191783Srmacklem/* 1110191783Srmacklem * VFS Operations. 1111191783Srmacklem * 1112191783Srmacklem * mount system call 1113191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then 1114191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before 1115191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and 1116191783Srmacklem * an error after that means that I have to release the mbuf. 1117191783Srmacklem */ 1118191783Srmacklem/* ARGSUSED */ 1119191783Srmacklemstatic int 1120191990Sattilionfs_cmount(struct mntarg *ma, void *data, int flags) 1121191783Srmacklem{ 1122191783Srmacklem int error; 1123191783Srmacklem struct nfs_args args; 1124191783Srmacklem 1125191783Srmacklem error = copyin(data, &args, sizeof (struct nfs_args)); 1126191783Srmacklem if (error) 1127191783Srmacklem return error; 1128191783Srmacklem 1129191783Srmacklem ma = mount_arg(ma, "nfs_args", &args, sizeof args); 1130191783Srmacklem 1131191783Srmacklem error = kernel_mount(ma, flags); 1132191783Srmacklem return (error); 1133191783Srmacklem} 1134191783Srmacklem 1135191783Srmacklem/* 1136191783Srmacklem * Common code for mount and mountroot 1137191783Srmacklem */ 1138191783Srmacklemstatic int 1139191783Srmacklemmountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam, 1140221014Srmacklem char *hst, u_char *krbname, int krbnamelen, u_char *dirpath, int dirlen, 1141221014Srmacklem u_char *srvkrbname, int srvkrbnamelen, struct vnode **vpp, 1142221014Srmacklem struct ucred *cred, struct thread *td, int negnametimeo) 1143191783Srmacklem{ 1144191783Srmacklem struct nfsmount *nmp; 1145191783Srmacklem struct nfsnode *np; 1146195762Srmacklem int error, trycnt, ret; 1147191783Srmacklem struct nfsvattr nfsva; 1148191783Srmacklem static u_int64_t clval = 0; 1149191783Srmacklem 1150191783Srmacklem if (mp->mnt_flag & MNT_UPDATE) { 1151191783Srmacklem nmp = VFSTONFS(mp); 1152191783Srmacklem printf("%s: MNT_UPDATE is no longer handled here\n", __func__); 1153191783Srmacklem FREE(nam, M_SONAME); 1154191783Srmacklem return (0); 1155191783Srmacklem } else { 1156191783Srmacklem MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount) + 1157221014Srmacklem krbnamelen + dirlen + srvkrbnamelen + 2, 1158221014Srmacklem M_NEWNFSMNT, M_WAITOK | M_ZERO); 1159191783Srmacklem TAILQ_INIT(&nmp->nm_bufq); 1160191783Srmacklem if (clval == 0) 1161191783Srmacklem clval = (u_int64_t)nfsboottime.tv_sec; 1162191783Srmacklem nmp->nm_clval = clval++; 1163221014Srmacklem nmp->nm_krbnamelen = krbnamelen; 1164221014Srmacklem nmp->nm_dirpathlen = dirlen; 1165221014Srmacklem nmp->nm_srvkrbnamelen = srvkrbnamelen; 1166192675Srmacklem if (td->td_ucred->cr_uid != (uid_t)0) { 1167191783Srmacklem /* 1168192675Srmacklem * nm_uid is used to get KerberosV credentials for 1169192675Srmacklem * the nfsv4 state handling operations if there is 1170192675Srmacklem * no host based principal set. Use the uid of 1171192675Srmacklem * this user if not root, since they are doing the 1172192675Srmacklem * mount. I don't think setting this for root will 1173192675Srmacklem * work, since root normally does not have user 1174192675Srmacklem * credentials in a credentials cache. 1175191783Srmacklem */ 1176192675Srmacklem nmp->nm_uid = td->td_ucred->cr_uid; 1177191783Srmacklem } else { 1178191783Srmacklem /* 1179192675Srmacklem * Just set to -1, so it won't be used. 1180191783Srmacklem */ 1181191783Srmacklem nmp->nm_uid = (uid_t)-1; 1182191783Srmacklem } 1183191783Srmacklem 1184191783Srmacklem /* Copy and null terminate all the names */ 1185191783Srmacklem if (nmp->nm_krbnamelen > 0) { 1186191783Srmacklem bcopy(krbname, nmp->nm_krbname, nmp->nm_krbnamelen); 1187191783Srmacklem nmp->nm_name[nmp->nm_krbnamelen] = '\0'; 1188191783Srmacklem } 1189191783Srmacklem if (nmp->nm_dirpathlen > 0) { 1190191783Srmacklem bcopy(dirpath, NFSMNT_DIRPATH(nmp), 1191191783Srmacklem nmp->nm_dirpathlen); 1192191783Srmacklem nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen 1193191783Srmacklem + 1] = '\0'; 1194191783Srmacklem } 1195191783Srmacklem if (nmp->nm_srvkrbnamelen > 0) { 1196191783Srmacklem bcopy(srvkrbname, NFSMNT_SRVKRBNAME(nmp), 1197191783Srmacklem nmp->nm_srvkrbnamelen); 1198191783Srmacklem nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen 1199191783Srmacklem + nmp->nm_srvkrbnamelen + 2] = '\0'; 1200191783Srmacklem } 1201191783Srmacklem nmp->nm_sockreq.nr_cred = crhold(cred); 1202191783Srmacklem mtx_init(&nmp->nm_sockreq.nr_mtx, "nfssock", NULL, MTX_DEF); 1203191783Srmacklem mp->mnt_data = nmp; 1204214048Srmacklem nmp->nm_getinfo = nfs_getnlminfo; 1205216931Srmacklem nmp->nm_vinvalbuf = ncl_vinvalbuf; 1206191783Srmacklem } 1207191783Srmacklem vfs_getnewfsid(mp); 1208191783Srmacklem nmp->nm_mountp = mp; 1209191783Srmacklem mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK); 1210203303Srmacklem nmp->nm_negnametimeo = negnametimeo; 1211191783Srmacklem 1212214048Srmacklem nfs_decode_args(mp, nmp, argp, hst, cred, td); 1213192585Srmacklem 1214191783Srmacklem /* 1215191783Srmacklem * V2 can only handle 32 bit filesizes. A 4GB-1 limit may be too 1216191783Srmacklem * high, depending on whether we end up with negative offsets in 1217191783Srmacklem * the client or server somewhere. 2GB-1 may be safer. 1218191783Srmacklem * 1219191783Srmacklem * For V3, ncl_fsinfo will adjust this as necessary. Assume maximum 1220191783Srmacklem * that we can handle until we find out otherwise. 1221191783Srmacklem * XXX Our "safe" limit on the client is what we can store in our 1222191783Srmacklem * buffer cache using signed(!) block numbers. 1223191783Srmacklem */ 1224191783Srmacklem if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) 1225191783Srmacklem nmp->nm_maxfilesize = 0xffffffffLL; 1226191783Srmacklem else 1227221537Srmacklem nmp->nm_maxfilesize = OFF_MAX; 1228191783Srmacklem 1229191783Srmacklem nmp->nm_timeo = NFS_TIMEO; 1230191783Srmacklem nmp->nm_retry = NFS_RETRANS; 1231191783Srmacklem if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) { 1232191783Srmacklem nmp->nm_wsize = NFS_WSIZE; 1233191783Srmacklem nmp->nm_rsize = NFS_RSIZE; 1234191783Srmacklem nmp->nm_readdirsize = NFS_READDIRSIZE; 1235191783Srmacklem } 1236191783Srmacklem nmp->nm_wcommitsize = hibufspace / (desiredvnodes / 1000); 1237191783Srmacklem nmp->nm_numgrps = NFS_MAXGRPS; 1238191783Srmacklem nmp->nm_readahead = NFS_DEFRAHEAD; 1239191783Srmacklem nmp->nm_tprintf_delay = nfs_tprintf_delay; 1240191783Srmacklem if (nmp->nm_tprintf_delay < 0) 1241191783Srmacklem nmp->nm_tprintf_delay = 0; 1242191783Srmacklem nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay; 1243191783Srmacklem if (nmp->nm_tprintf_initial_delay < 0) 1244191783Srmacklem nmp->nm_tprintf_initial_delay = 0; 1245191783Srmacklem nmp->nm_fhsize = argp->fhsize; 1246191783Srmacklem if (nmp->nm_fhsize > 0) 1247191783Srmacklem bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize); 1248191783Srmacklem bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN); 1249191783Srmacklem nmp->nm_nam = nam; 1250191783Srmacklem /* Set up the sockets and per-host congestion */ 1251191783Srmacklem nmp->nm_sotype = argp->sotype; 1252191783Srmacklem nmp->nm_soproto = argp->proto; 1253191783Srmacklem nmp->nm_sockreq.nr_prog = NFS_PROG; 1254191783Srmacklem if ((argp->flags & NFSMNT_NFSV4)) 1255191783Srmacklem nmp->nm_sockreq.nr_vers = NFS_VER4; 1256191783Srmacklem else if ((argp->flags & NFSMNT_NFSV3)) 1257191783Srmacklem nmp->nm_sockreq.nr_vers = NFS_VER3; 1258191783Srmacklem else 1259191783Srmacklem nmp->nm_sockreq.nr_vers = NFS_VER2; 1260191783Srmacklem 1261191783Srmacklem 1262191783Srmacklem if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0))) 1263191783Srmacklem goto bad; 1264191783Srmacklem 1265191783Srmacklem /* 1266191783Srmacklem * A reference count is needed on the nfsnode representing the 1267191783Srmacklem * remote root. If this object is not persistent, then backward 1268191783Srmacklem * traversals of the mount point (i.e. "..") will not work if 1269191783Srmacklem * the nfsnode gets flushed out of the cache. Ufs does not have 1270191783Srmacklem * this problem, because one can identify root inodes by their 1271191783Srmacklem * number == ROOTINO (2). 1272191783Srmacklem */ 1273191783Srmacklem if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) && 1274191783Srmacklem nmp->nm_dirpathlen > 0) { 1275191783Srmacklem /* 1276191783Srmacklem * If the fhsize on the mount point == 0 for V4, the mount 1277191783Srmacklem * path needs to be looked up. 1278191783Srmacklem */ 1279191783Srmacklem trycnt = 3; 1280191783Srmacklem do { 1281191783Srmacklem error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp), 1282191783Srmacklem cred, td); 1283191783Srmacklem if (error) 1284207170Srmacklem (void) nfs_catnap(PZERO, error, "nfsgetdirp"); 1285191783Srmacklem } while (error && --trycnt > 0); 1286191783Srmacklem if (error) { 1287191783Srmacklem error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); 1288191783Srmacklem goto bad; 1289191783Srmacklem } 1290191783Srmacklem } 1291191783Srmacklem if (nmp->nm_fhsize > 0) { 1292195762Srmacklem /* 1293195762Srmacklem * Set f_iosize to NFS_DIRBLKSIZ so that bo_bsize gets set 1294195762Srmacklem * non-zero for the root vnode. f_iosize will be set correctly 1295195762Srmacklem * by nfs_statfs() before any I/O occurs. 1296195762Srmacklem */ 1297195762Srmacklem mp->mnt_stat.f_iosize = NFS_DIRBLKSIZ; 1298220732Srmacklem error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, 1299220732Srmacklem LK_EXCLUSIVE); 1300191783Srmacklem if (error) 1301191783Srmacklem goto bad; 1302191783Srmacklem *vpp = NFSTOV(np); 1303191783Srmacklem 1304191783Srmacklem /* 1305191783Srmacklem * Get file attributes and transfer parameters for the 1306191783Srmacklem * mountpoint. This has the side effect of filling in 1307191783Srmacklem * (*vpp)->v_type with the correct value. 1308191783Srmacklem */ 1309191783Srmacklem ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1, 1310191783Srmacklem cred, td, &nfsva, NULL); 1311191783Srmacklem if (ret) { 1312191783Srmacklem /* 1313191783Srmacklem * Just set default values to get things going. 1314191783Srmacklem */ 1315191783Srmacklem NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr)); 1316191783Srmacklem nfsva.na_vattr.va_type = VDIR; 1317191783Srmacklem nfsva.na_vattr.va_mode = 0777; 1318191783Srmacklem nfsva.na_vattr.va_nlink = 100; 1319191783Srmacklem nfsva.na_vattr.va_uid = (uid_t)0; 1320191783Srmacklem nfsva.na_vattr.va_gid = (gid_t)0; 1321191783Srmacklem nfsva.na_vattr.va_fileid = 2; 1322191783Srmacklem nfsva.na_vattr.va_gen = 1; 1323191783Srmacklem nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE; 1324191783Srmacklem nfsva.na_vattr.va_size = 512 * 1024; 1325191783Srmacklem } 1326191783Srmacklem (void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1); 1327191783Srmacklem if (argp->flags & NFSMNT_NFSV3) 1328191783Srmacklem ncl_fsinfo(nmp, *vpp, cred, td); 1329191783Srmacklem 1330191783Srmacklem /* 1331191783Srmacklem * Lose the lock but keep the ref. 1332191783Srmacklem */ 1333191783Srmacklem VOP_UNLOCK(*vpp, 0); 1334191783Srmacklem return (0); 1335191783Srmacklem } 1336191783Srmacklem error = EIO; 1337191783Srmacklem 1338191783Srmacklembad: 1339191783Srmacklem newnfs_disconnect(&nmp->nm_sockreq); 1340191783Srmacklem crfree(nmp->nm_sockreq.nr_cred); 1341191783Srmacklem mtx_destroy(&nmp->nm_sockreq.nr_mtx); 1342191783Srmacklem mtx_destroy(&nmp->nm_mtx); 1343191783Srmacklem FREE(nmp, M_NEWNFSMNT); 1344191783Srmacklem FREE(nam, M_SONAME); 1345191783Srmacklem return (error); 1346191783Srmacklem} 1347191783Srmacklem 1348191783Srmacklem/* 1349191783Srmacklem * unmount system call 1350191783Srmacklem */ 1351191783Srmacklemstatic int 1352191990Sattilionfs_unmount(struct mount *mp, int mntflags) 1353191783Srmacklem{ 1354191990Sattilio struct thread *td; 1355191783Srmacklem struct nfsmount *nmp; 1356191783Srmacklem int error, flags = 0, trycnt = 0; 1357191783Srmacklem 1358191990Sattilio td = curthread; 1359191990Sattilio 1360191783Srmacklem if (mntflags & MNT_FORCE) 1361191783Srmacklem flags |= FORCECLOSE; 1362191783Srmacklem nmp = VFSTONFS(mp); 1363191783Srmacklem /* 1364191783Srmacklem * Goes something like this.. 1365191783Srmacklem * - Call vflush() to clear out vnodes for this filesystem 1366191783Srmacklem * - Close the socket 1367191783Srmacklem * - Free up the data structures 1368191783Srmacklem */ 1369191783Srmacklem /* In the forced case, cancel any outstanding requests. */ 1370191783Srmacklem if (mntflags & MNT_FORCE) { 1371191783Srmacklem error = newnfs_nmcancelreqs(nmp); 1372191783Srmacklem if (error) 1373191783Srmacklem goto out; 1374191783Srmacklem /* For a forced close, get rid of the renew thread now */ 1375191783Srmacklem nfscl_umount(nmp, td); 1376191783Srmacklem } 1377191783Srmacklem /* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */ 1378191783Srmacklem do { 1379191783Srmacklem error = vflush(mp, 1, flags, td); 1380191783Srmacklem if ((mntflags & MNT_FORCE) && error != 0 && ++trycnt < 30) 1381207170Srmacklem (void) nfs_catnap(PSOCK, error, "newndm"); 1382191783Srmacklem } while ((mntflags & MNT_FORCE) && error != 0 && trycnt < 30); 1383191783Srmacklem if (error) 1384191783Srmacklem goto out; 1385191783Srmacklem 1386191783Srmacklem /* 1387191783Srmacklem * We are now committed to the unmount. 1388191783Srmacklem */ 1389191783Srmacklem if ((mntflags & MNT_FORCE) == 0) 1390191783Srmacklem nfscl_umount(nmp, td); 1391191783Srmacklem newnfs_disconnect(&nmp->nm_sockreq); 1392191783Srmacklem crfree(nmp->nm_sockreq.nr_cred); 1393191783Srmacklem FREE(nmp->nm_nam, M_SONAME); 1394191783Srmacklem 1395191783Srmacklem mtx_destroy(&nmp->nm_sockreq.nr_mtx); 1396191783Srmacklem mtx_destroy(&nmp->nm_mtx); 1397191783Srmacklem FREE(nmp, M_NEWNFSMNT); 1398191783Srmacklemout: 1399191783Srmacklem return (error); 1400191783Srmacklem} 1401191783Srmacklem 1402191783Srmacklem/* 1403191783Srmacklem * Return root of a filesystem 1404191783Srmacklem */ 1405191783Srmacklemstatic int 1406191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp) 1407191783Srmacklem{ 1408191783Srmacklem struct vnode *vp; 1409191783Srmacklem struct nfsmount *nmp; 1410191783Srmacklem struct nfsnode *np; 1411191783Srmacklem int error; 1412191783Srmacklem 1413191783Srmacklem nmp = VFSTONFS(mp); 1414220732Srmacklem error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np, flags); 1415191783Srmacklem if (error) 1416191783Srmacklem return error; 1417191783Srmacklem vp = NFSTOV(np); 1418191783Srmacklem /* 1419191783Srmacklem * Get transfer parameters and attributes for root vnode once. 1420191783Srmacklem */ 1421191783Srmacklem mtx_lock(&nmp->nm_mtx); 1422191783Srmacklem if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) { 1423191783Srmacklem mtx_unlock(&nmp->nm_mtx); 1424191783Srmacklem ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread); 1425191783Srmacklem } else 1426191783Srmacklem mtx_unlock(&nmp->nm_mtx); 1427191783Srmacklem if (vp->v_type == VNON) 1428191783Srmacklem vp->v_type = VDIR; 1429191783Srmacklem vp->v_vflag |= VV_ROOT; 1430191783Srmacklem *vpp = vp; 1431191783Srmacklem return (0); 1432191783Srmacklem} 1433191783Srmacklem 1434191783Srmacklem/* 1435191783Srmacklem * Flush out the buffer cache 1436191783Srmacklem */ 1437191783Srmacklem/* ARGSUSED */ 1438191783Srmacklemstatic int 1439191990Sattilionfs_sync(struct mount *mp, int waitfor) 1440191783Srmacklem{ 1441191783Srmacklem struct vnode *vp, *mvp; 1442191990Sattilio struct thread *td; 1443191783Srmacklem int error, allerror = 0; 1444191783Srmacklem 1445191990Sattilio td = curthread; 1446191990Sattilio 1447191783Srmacklem /* 1448191783Srmacklem * Force stale buffer cache information to be flushed. 1449191783Srmacklem */ 1450191783Srmacklem MNT_ILOCK(mp); 1451191783Srmacklemloop: 1452191783Srmacklem MNT_VNODE_FOREACH(vp, mp, mvp) { 1453191783Srmacklem VI_LOCK(vp); 1454191783Srmacklem MNT_IUNLOCK(mp); 1455191783Srmacklem /* XXX Racy bv_cnt check. */ 1456191783Srmacklem if (VOP_ISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 || 1457191783Srmacklem waitfor == MNT_LAZY) { 1458191783Srmacklem VI_UNLOCK(vp); 1459191783Srmacklem MNT_ILOCK(mp); 1460191783Srmacklem continue; 1461191783Srmacklem } 1462191783Srmacklem if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) { 1463191783Srmacklem MNT_ILOCK(mp); 1464191783Srmacklem MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp); 1465191783Srmacklem goto loop; 1466191783Srmacklem } 1467191783Srmacklem error = VOP_FSYNC(vp, waitfor, td); 1468191783Srmacklem if (error) 1469191783Srmacklem allerror = error; 1470191783Srmacklem VOP_UNLOCK(vp, 0); 1471191783Srmacklem vrele(vp); 1472191783Srmacklem 1473191783Srmacklem MNT_ILOCK(mp); 1474191783Srmacklem } 1475191783Srmacklem MNT_IUNLOCK(mp); 1476191783Srmacklem return (allerror); 1477191783Srmacklem} 1478191783Srmacklem 1479191783Srmacklemstatic int 1480191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req) 1481191783Srmacklem{ 1482191783Srmacklem struct nfsmount *nmp = VFSTONFS(mp); 1483191783Srmacklem struct vfsquery vq; 1484191783Srmacklem int error; 1485191783Srmacklem 1486191783Srmacklem bzero(&vq, sizeof(vq)); 1487191783Srmacklem switch (op) { 1488191783Srmacklem#if 0 1489191783Srmacklem case VFS_CTL_NOLOCKS: 1490191783Srmacklem val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0; 1491191783Srmacklem if (req->oldptr != NULL) { 1492191783Srmacklem error = SYSCTL_OUT(req, &val, sizeof(val)); 1493191783Srmacklem if (error) 1494191783Srmacklem return (error); 1495191783Srmacklem } 1496191783Srmacklem if (req->newptr != NULL) { 1497191783Srmacklem error = SYSCTL_IN(req, &val, sizeof(val)); 1498191783Srmacklem if (error) 1499191783Srmacklem return (error); 1500191783Srmacklem if (val) 1501191783Srmacklem nmp->nm_flag |= NFSMNT_NOLOCKS; 1502191783Srmacklem else 1503191783Srmacklem nmp->nm_flag &= ~NFSMNT_NOLOCKS; 1504191783Srmacklem } 1505191783Srmacklem break; 1506191783Srmacklem#endif 1507191783Srmacklem case VFS_CTL_QUERY: 1508191783Srmacklem mtx_lock(&nmp->nm_mtx); 1509191783Srmacklem if (nmp->nm_state & NFSSTA_TIMEO) 1510191783Srmacklem vq.vq_flags |= VQ_NOTRESP; 1511191783Srmacklem mtx_unlock(&nmp->nm_mtx); 1512191783Srmacklem#if 0 1513191783Srmacklem if (!(nmp->nm_flag & NFSMNT_NOLOCKS) && 1514191783Srmacklem (nmp->nm_state & NFSSTA_LOCKTIMEO)) 1515191783Srmacklem vq.vq_flags |= VQ_NOTRESPLOCK; 1516191783Srmacklem#endif 1517191783Srmacklem error = SYSCTL_OUT(req, &vq, sizeof(vq)); 1518191783Srmacklem break; 1519191783Srmacklem case VFS_CTL_TIMEO: 1520191783Srmacklem if (req->oldptr != NULL) { 1521191783Srmacklem error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay, 1522191783Srmacklem sizeof(nmp->nm_tprintf_initial_delay)); 1523191783Srmacklem if (error) 1524191783Srmacklem return (error); 1525191783Srmacklem } 1526191783Srmacklem if (req->newptr != NULL) { 1527191783Srmacklem error = vfs_suser(mp, req->td); 1528191783Srmacklem if (error) 1529191783Srmacklem return (error); 1530191783Srmacklem error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay, 1531191783Srmacklem sizeof(nmp->nm_tprintf_initial_delay)); 1532191783Srmacklem if (error) 1533191783Srmacklem return (error); 1534191783Srmacklem if (nmp->nm_tprintf_initial_delay < 0) 1535191783Srmacklem nmp->nm_tprintf_initial_delay = 0; 1536191783Srmacklem } 1537191783Srmacklem break; 1538191783Srmacklem default: 1539191783Srmacklem return (ENOTSUP); 1540191783Srmacklem } 1541191783Srmacklem return (0); 1542191783Srmacklem} 1543191783Srmacklem 1544214048Srmacklem/* 1545214048Srmacklem * Extract the information needed by the nlm from the nfs vnode. 1546214048Srmacklem */ 1547214048Srmacklemstatic void 1548214053Srmacklemnfs_getnlminfo(struct vnode *vp, uint8_t *fhp, size_t *fhlenp, 1549216931Srmacklem struct sockaddr_storage *sp, int *is_v3p, off_t *sizep, 1550216931Srmacklem struct timeval *timeop) 1551214048Srmacklem{ 1552214048Srmacklem struct nfsmount *nmp; 1553214048Srmacklem struct nfsnode *np = VTONFS(vp); 1554214048Srmacklem 1555214048Srmacklem nmp = VFSTONFS(vp->v_mount); 1556214048Srmacklem if (fhlenp != NULL) 1557214053Srmacklem *fhlenp = (size_t)np->n_fhp->nfh_len; 1558214048Srmacklem if (fhp != NULL) 1559214048Srmacklem bcopy(np->n_fhp->nfh_fh, fhp, np->n_fhp->nfh_len); 1560214048Srmacklem if (sp != NULL) 1561214048Srmacklem bcopy(nmp->nm_nam, sp, min(nmp->nm_nam->sa_len, sizeof(*sp))); 1562214048Srmacklem if (is_v3p != NULL) 1563214048Srmacklem *is_v3p = NFS_ISV3(vp); 1564214048Srmacklem if (sizep != NULL) 1565214048Srmacklem *sizep = np->n_size; 1566216931Srmacklem if (timeop != NULL) { 1567216931Srmacklem timeop->tv_sec = nmp->nm_timeo / NFS_HZ; 1568216931Srmacklem timeop->tv_usec = (nmp->nm_timeo % NFS_HZ) * (1000000 / NFS_HZ); 1569216931Srmacklem } 1570214048Srmacklem} 1571214048Srmacklem 1572