nfs_clvfsops.c revision 218757
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 218757 2011-02-16 21:29:13Z bz $"); 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> 49191783Srmacklem#include <sys/lock.h> 50191783Srmacklem#include <sys/malloc.h> 51191783Srmacklem#include <sys/mbuf.h> 52191783Srmacklem#include <sys/module.h> 53191783Srmacklem#include <sys/mount.h> 54191783Srmacklem#include <sys/proc.h> 55191783Srmacklem#include <sys/socket.h> 56191783Srmacklem#include <sys/socketvar.h> 57191783Srmacklem#include <sys/sockio.h> 58191783Srmacklem#include <sys/sysctl.h> 59191783Srmacklem#include <sys/vnode.h> 60191783Srmacklem#include <sys/signalvar.h> 61191783Srmacklem 62191783Srmacklem#include <vm/vm.h> 63191783Srmacklem#include <vm/vm_extern.h> 64191783Srmacklem#include <vm/uma.h> 65191783Srmacklem 66191783Srmacklem#include <net/if.h> 67191783Srmacklem#include <net/route.h> 68191783Srmacklem#include <netinet/in.h> 69191783Srmacklem 70191783Srmacklem#include <fs/nfs/nfsport.h> 71191783Srmacklem#include <fs/nfsclient/nfsnode.h> 72191783Srmacklem#include <fs/nfsclient/nfsmount.h> 73191783Srmacklem#include <fs/nfsclient/nfs.h> 74191783Srmacklem#include <fs/nfsclient/nfsdiskless.h> 75191783Srmacklem 76191783Srmacklemextern int nfscl_ticks; 77191783Srmacklemextern struct timeval nfsboottime; 78191783Srmacklemextern struct nfsstats newnfsstats; 79191783Srmacklem 80191783SrmacklemMALLOC_DEFINE(M_NEWNFSREQ, "newnfsclient_req", "New NFS request header"); 81191783SrmacklemMALLOC_DEFINE(M_NEWNFSMNT, "newnfsmnt", "New NFS mount struct"); 82191783Srmacklem 83191783SrmacklemSYSCTL_DECL(_vfs_newnfs); 84191783SrmacklemSYSCTL_STRUCT(_vfs_newnfs, NFS_NFSSTATS, nfsstats, CTLFLAG_RW, 85191783Srmacklem &newnfsstats, nfsstats, "S,nfsstats"); 86191783Srmacklemstatic int nfs_ip_paranoia = 1; 87191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, nfs_ip_paranoia, CTLFLAG_RW, 88191783Srmacklem &nfs_ip_paranoia, 0, ""); 89191783Srmacklemstatic int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY; 90191783SrmacklemSYSCTL_INT(_vfs_newnfs, NFS_TPRINTF_INITIAL_DELAY, 91191783Srmacklem downdelayinitial, CTLFLAG_RW, &nfs_tprintf_initial_delay, 0, ""); 92191783Srmacklem/* how long between console messages "nfs server foo not responding" */ 93191783Srmacklemstatic int nfs_tprintf_delay = NFS_TPRINTF_DELAY; 94191783SrmacklemSYSCTL_INT(_vfs_newnfs, NFS_TPRINTF_DELAY, 95191783Srmacklem downdelayinterval, CTLFLAG_RW, &nfs_tprintf_delay, 0, ""); 96191783Srmacklem 97192585Srmacklemstatic void nfs_sec_name(char *, int *); 98191783Srmacklemstatic void nfs_decode_args(struct mount *mp, struct nfsmount *nmp, 99214048Srmacklem struct nfs_args *argp, const char *, struct ucred *, 100214048Srmacklem struct thread *); 101191783Srmacklemstatic int mountnfs(struct nfs_args *, struct mount *, 102191783Srmacklem struct sockaddr *, char *, u_char *, u_char *, u_char *, 103203303Srmacklem struct vnode **, struct ucred *, struct thread *, int); 104214053Srmacklemstatic void nfs_getnlminfo(struct vnode *, uint8_t *, size_t *, 105216931Srmacklem struct sockaddr_storage *, int *, off_t *, 106216931Srmacklem struct timeval *); 107191783Srmacklemstatic vfs_mount_t nfs_mount; 108191783Srmacklemstatic vfs_cmount_t nfs_cmount; 109191783Srmacklemstatic vfs_unmount_t nfs_unmount; 110191783Srmacklemstatic vfs_root_t nfs_root; 111191783Srmacklemstatic vfs_statfs_t nfs_statfs; 112191783Srmacklemstatic vfs_sync_t nfs_sync; 113191783Srmacklemstatic vfs_sysctl_t nfs_sysctl; 114191783Srmacklem 115191783Srmacklem/* 116191783Srmacklem * nfs vfs operations. 117191783Srmacklem */ 118191783Srmacklemstatic struct vfsops nfs_vfsops = { 119191783Srmacklem .vfs_init = ncl_init, 120191783Srmacklem .vfs_mount = nfs_mount, 121191783Srmacklem .vfs_cmount = nfs_cmount, 122191783Srmacklem .vfs_root = nfs_root, 123191783Srmacklem .vfs_statfs = nfs_statfs, 124191783Srmacklem .vfs_sync = nfs_sync, 125191783Srmacklem .vfs_uninit = ncl_uninit, 126191783Srmacklem .vfs_unmount = nfs_unmount, 127191783Srmacklem .vfs_sysctl = nfs_sysctl, 128191783Srmacklem}; 129191783SrmacklemVFS_SET(nfs_vfsops, newnfs, VFCF_NETWORK); 130191783Srmacklem 131191783Srmacklem/* So that loader and kldload(2) can find us, wherever we are.. */ 132191783SrmacklemMODULE_VERSION(newnfs, 1); 133191783Srmacklem 134191783Srmacklem/* 135191783Srmacklem * This structure must be filled in by a primary bootstrap or bootstrap 136191783Srmacklem * server for a diskless/dataless machine. It is initialized below just 137191783Srmacklem * to ensure that it is allocated to initialized data (.data not .bss). 138191783Srmacklem */ 139191783Srmacklemstruct nfs_diskless newnfs_diskless = { { { 0 } } }; 140191783Srmacklemstruct nfsv3_diskless newnfsv3_diskless = { { { 0 } } }; 141191783Srmacklemint newnfs_diskless_valid = 0; 142191783Srmacklem 143191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, diskless_valid, CTLFLAG_RD, 144192145Srmacklem &newnfs_diskless_valid, 0, 145192145Srmacklem "Has the diskless struct been filled correctly"); 146191783Srmacklem 147191783SrmacklemSYSCTL_STRING(_vfs_newnfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD, 148192145Srmacklem newnfsv3_diskless.root_hostnam, 0, "Path to nfs root"); 149191783Srmacklem 150191783SrmacklemSYSCTL_OPAQUE(_vfs_newnfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD, 151192145Srmacklem &newnfsv3_diskless.root_saddr, sizeof newnfsv3_diskless.root_saddr, 152192145Srmacklem "%Ssockaddr_in", "Diskless root nfs address"); 153191783Srmacklem 154191783Srmacklem 155191783Srmacklemvoid newnfsargs_ntoh(struct nfs_args *); 156191783Srmacklemstatic int nfs_mountdiskless(char *, 157191783Srmacklem struct sockaddr_in *, struct nfs_args *, 158191783Srmacklem struct thread *, struct vnode **, struct mount *); 159191783Srmacklemstatic void nfs_convert_diskless(void); 160191783Srmacklemstatic void nfs_convert_oargs(struct nfs_args *args, 161191783Srmacklem struct onfs_args *oargs); 162191783Srmacklem 163191783Srmacklemint 164191783Srmacklemnewnfs_iosize(struct nfsmount *nmp) 165191783Srmacklem{ 166191783Srmacklem int iosize, maxio; 167191783Srmacklem 168191783Srmacklem /* First, set the upper limit for iosize */ 169191783Srmacklem if (nmp->nm_flag & NFSMNT_NFSV4) { 170191783Srmacklem maxio = NFS_MAXBSIZE; 171191783Srmacklem } else if (nmp->nm_flag & NFSMNT_NFSV3) { 172191783Srmacklem if (nmp->nm_sotype == SOCK_DGRAM) 173191783Srmacklem maxio = NFS_MAXDGRAMDATA; 174191783Srmacklem else 175191783Srmacklem maxio = NFS_MAXBSIZE; 176191783Srmacklem } else { 177191783Srmacklem maxio = NFS_V2MAXDATA; 178191783Srmacklem } 179191783Srmacklem if (nmp->nm_rsize > maxio || nmp->nm_rsize == 0) 180191783Srmacklem nmp->nm_rsize = maxio; 181191783Srmacklem if (nmp->nm_rsize > MAXBSIZE) 182191783Srmacklem nmp->nm_rsize = MAXBSIZE; 183191783Srmacklem if (nmp->nm_readdirsize > maxio || nmp->nm_readdirsize == 0) 184191783Srmacklem nmp->nm_readdirsize = maxio; 185191783Srmacklem if (nmp->nm_readdirsize > nmp->nm_rsize) 186191783Srmacklem nmp->nm_readdirsize = nmp->nm_rsize; 187191783Srmacklem if (nmp->nm_wsize > maxio || nmp->nm_wsize == 0) 188191783Srmacklem nmp->nm_wsize = maxio; 189191783Srmacklem if (nmp->nm_wsize > MAXBSIZE) 190191783Srmacklem nmp->nm_wsize = MAXBSIZE; 191191783Srmacklem 192191783Srmacklem /* 193191783Srmacklem * Calculate the size used for io buffers. Use the larger 194191783Srmacklem * of the two sizes to minimise nfs requests but make sure 195191783Srmacklem * that it is at least one VM page to avoid wasting buffer 196191783Srmacklem * space. 197191783Srmacklem */ 198191783Srmacklem iosize = imax(nmp->nm_rsize, nmp->nm_wsize); 199191783Srmacklem iosize = imax(iosize, PAGE_SIZE); 200191783Srmacklem nmp->nm_mountp->mnt_stat.f_iosize = iosize; 201191783Srmacklem return (iosize); 202191783Srmacklem} 203191783Srmacklem 204191783Srmacklemstatic void 205191783Srmacklemnfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs) 206191783Srmacklem{ 207191783Srmacklem 208191783Srmacklem args->version = NFS_ARGSVERSION; 209191783Srmacklem args->addr = oargs->addr; 210191783Srmacklem args->addrlen = oargs->addrlen; 211191783Srmacklem args->sotype = oargs->sotype; 212191783Srmacklem args->proto = oargs->proto; 213191783Srmacklem args->fh = oargs->fh; 214191783Srmacklem args->fhsize = oargs->fhsize; 215191783Srmacklem args->flags = oargs->flags; 216191783Srmacklem args->wsize = oargs->wsize; 217191783Srmacklem args->rsize = oargs->rsize; 218191783Srmacklem args->readdirsize = oargs->readdirsize; 219191783Srmacklem args->timeo = oargs->timeo; 220191783Srmacklem args->retrans = oargs->retrans; 221191783Srmacklem args->readahead = oargs->readahead; 222191783Srmacklem args->hostname = oargs->hostname; 223191783Srmacklem} 224191783Srmacklem 225191783Srmacklemstatic void 226191783Srmacklemnfs_convert_diskless(void) 227191783Srmacklem{ 228191783Srmacklem 229191783Srmacklem bcopy(&newnfs_diskless.myif, &newnfsv3_diskless.myif, 230192145Srmacklem sizeof (struct ifaliasreq)); 231191783Srmacklem bcopy(&newnfs_diskless.mygateway, &newnfsv3_diskless.mygateway, 232192145Srmacklem sizeof (struct sockaddr_in)); 233192145Srmacklem nfs_convert_oargs(&newnfsv3_diskless.root_args, 234192145Srmacklem &newnfs_diskless.root_args); 235191783Srmacklem if (newnfsv3_diskless.root_args.flags & NFSMNT_NFSV3) { 236191783Srmacklem newnfsv3_diskless.root_fhsize = NFSX_MYFH; 237192145Srmacklem bcopy(newnfs_diskless.root_fh, newnfsv3_diskless.root_fh, 238192145Srmacklem NFSX_MYFH); 239191783Srmacklem } else { 240191783Srmacklem newnfsv3_diskless.root_fhsize = NFSX_V2FH; 241192145Srmacklem bcopy(newnfs_diskless.root_fh, newnfsv3_diskless.root_fh, 242192145Srmacklem NFSX_V2FH); 243191783Srmacklem } 244191783Srmacklem bcopy(&newnfs_diskless.root_saddr,&newnfsv3_diskless.root_saddr, 245192145Srmacklem sizeof(struct sockaddr_in)); 246192145Srmacklem bcopy(newnfs_diskless.root_hostnam, newnfsv3_diskless.root_hostnam, 247192145Srmacklem MNAMELEN); 248191783Srmacklem newnfsv3_diskless.root_time = newnfs_diskless.root_time; 249191783Srmacklem bcopy(newnfs_diskless.my_hostnam, newnfsv3_diskless.my_hostnam, 250192145Srmacklem MAXHOSTNAMELEN); 251191783Srmacklem newnfs_diskless_valid = 3; 252191783Srmacklem} 253191783Srmacklem 254191783Srmacklem/* 255191783Srmacklem * nfs statfs call 256191783Srmacklem */ 257191783Srmacklemstatic int 258191990Sattilionfs_statfs(struct mount *mp, struct statfs *sbp) 259191783Srmacklem{ 260191783Srmacklem struct vnode *vp; 261191990Sattilio struct thread *td; 262191783Srmacklem struct nfsmount *nmp = VFSTONFS(mp); 263191783Srmacklem struct nfsvattr nfsva; 264191783Srmacklem struct nfsfsinfo fs; 265191783Srmacklem struct nfsstatfs sb; 266191783Srmacklem int error = 0, attrflag, gotfsinfo = 0, ret; 267191783Srmacklem struct nfsnode *np; 268191783Srmacklem 269191990Sattilio td = curthread; 270191990Sattilio 271191783Srmacklem error = vfs_busy(mp, MBF_NOWAIT); 272191783Srmacklem if (error) 273191783Srmacklem return (error); 274191783Srmacklem error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np); 275191783Srmacklem if (error) { 276191783Srmacklem vfs_unbusy(mp); 277191783Srmacklem return (error); 278191783Srmacklem } 279191783Srmacklem vp = NFSTOV(np); 280191783Srmacklem mtx_lock(&nmp->nm_mtx); 281191783Srmacklem if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) { 282191783Srmacklem mtx_unlock(&nmp->nm_mtx); 283191783Srmacklem error = nfsrpc_fsinfo(vp, &fs, td->td_ucred, td, &nfsva, 284191783Srmacklem &attrflag, NULL); 285191783Srmacklem if (!error) 286191783Srmacklem gotfsinfo = 1; 287191783Srmacklem } else 288191783Srmacklem mtx_unlock(&nmp->nm_mtx); 289191783Srmacklem if (!error) 290191783Srmacklem error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva, 291191783Srmacklem &attrflag, NULL); 292191783Srmacklem if (attrflag == 0) { 293191783Srmacklem ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1, 294191783Srmacklem td->td_ucred, td, &nfsva, NULL); 295191783Srmacklem if (ret) { 296191783Srmacklem /* 297191783Srmacklem * Just set default values to get things going. 298191783Srmacklem */ 299191783Srmacklem NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr)); 300191783Srmacklem nfsva.na_vattr.va_type = VDIR; 301191783Srmacklem nfsva.na_vattr.va_mode = 0777; 302191783Srmacklem nfsva.na_vattr.va_nlink = 100; 303191783Srmacklem nfsva.na_vattr.va_uid = (uid_t)0; 304191783Srmacklem nfsva.na_vattr.va_gid = (gid_t)0; 305191783Srmacklem nfsva.na_vattr.va_fileid = 2; 306191783Srmacklem nfsva.na_vattr.va_gen = 1; 307191783Srmacklem nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE; 308191783Srmacklem nfsva.na_vattr.va_size = 512 * 1024; 309191783Srmacklem } 310191783Srmacklem } 311191783Srmacklem (void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 1); 312191783Srmacklem if (!error) { 313191783Srmacklem mtx_lock(&nmp->nm_mtx); 314191783Srmacklem if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4)) 315191783Srmacklem nfscl_loadfsinfo(nmp, &fs); 316191783Srmacklem nfscl_loadsbinfo(nmp, &sb, sbp); 317191783Srmacklem sbp->f_flags = nmp->nm_flag; 318191783Srmacklem sbp->f_iosize = newnfs_iosize(nmp); 319191783Srmacklem mtx_unlock(&nmp->nm_mtx); 320191783Srmacklem if (sbp != &mp->mnt_stat) { 321191783Srmacklem bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); 322191783Srmacklem bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); 323191783Srmacklem } 324191783Srmacklem strncpy(&sbp->f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN); 325191783Srmacklem } else if (NFS_ISV4(vp)) { 326191783Srmacklem error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); 327191783Srmacklem } 328191783Srmacklem vput(vp); 329191783Srmacklem vfs_unbusy(mp); 330191783Srmacklem return (error); 331191783Srmacklem} 332191783Srmacklem 333191783Srmacklem/* 334191783Srmacklem * nfs version 3 fsinfo rpc call 335191783Srmacklem */ 336191783Srmacklemint 337191783Srmacklemncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred, 338191783Srmacklem struct thread *td) 339191783Srmacklem{ 340191783Srmacklem struct nfsfsinfo fs; 341191783Srmacklem struct nfsvattr nfsva; 342191783Srmacklem int error, attrflag; 343191783Srmacklem 344191783Srmacklem error = nfsrpc_fsinfo(vp, &fs, cred, td, &nfsva, &attrflag, NULL); 345191783Srmacklem if (!error) { 346191783Srmacklem if (attrflag) 347191783Srmacklem (void) nfscl_loadattrcache(&vp, &nfsva, NULL, NULL, 0, 348191783Srmacklem 1); 349191783Srmacklem mtx_lock(&nmp->nm_mtx); 350191783Srmacklem nfscl_loadfsinfo(nmp, &fs); 351191783Srmacklem mtx_unlock(&nmp->nm_mtx); 352191783Srmacklem } 353191783Srmacklem return (error); 354191783Srmacklem} 355191783Srmacklem 356191783Srmacklem/* 357191783Srmacklem * Mount a remote root fs via. nfs. This depends on the info in the 358191783Srmacklem * newnfs_diskless structure that has been filled in properly by some primary 359191783Srmacklem * bootstrap. 360191783Srmacklem * It goes something like this: 361191783Srmacklem * - do enough of "ifconfig" by calling ifioctl() so that the system 362191783Srmacklem * can talk to the server 363191783Srmacklem * - If newnfs_diskless.mygateway is filled in, use that address as 364191783Srmacklem * a default gateway. 365191783Srmacklem * - build the rootfs mount point and call mountnfs() to do the rest. 366191783Srmacklem * 367191783Srmacklem * It is assumed to be safe to read, modify, and write the nfsv3_diskless 368191783Srmacklem * structure, as well as other global NFS client variables here, as 369192145Srmacklem * nfs_mountroot() will be called once in the boot before any other NFS 370191783Srmacklem * client activity occurs. 371191783Srmacklem */ 372191783Srmacklemint 373192145Srmacklemncl_mountroot(struct mount *mp) 374191783Srmacklem{ 375192145Srmacklem struct thread *td = curthread; 376191783Srmacklem struct nfsv3_diskless *nd = &newnfsv3_diskless; 377191783Srmacklem struct socket *so; 378191783Srmacklem struct vnode *vp; 379191783Srmacklem struct ifreq ir; 380193066Sjamie int error; 381191783Srmacklem u_long l; 382191783Srmacklem char buf[128]; 383191783Srmacklem char *cp; 384191783Srmacklem 385191783Srmacklem#if defined(BOOTP_NFSROOT) && defined(BOOTP) 386192145Srmacklem bootpc_init(); /* use bootp to get nfs_diskless filled in */ 387191783Srmacklem#elif defined(NFS_ROOT) 388191783Srmacklem nfs_setup_diskless(); 389191783Srmacklem#endif 390191783Srmacklem 391191783Srmacklem if (newnfs_diskless_valid == 0) 392191783Srmacklem return (-1); 393191783Srmacklem if (newnfs_diskless_valid == 1) 394191783Srmacklem nfs_convert_diskless(); 395191783Srmacklem 396191783Srmacklem /* 397191783Srmacklem * XXX splnet, so networks will receive... 398191783Srmacklem */ 399191783Srmacklem splnet(); 400191783Srmacklem 401191783Srmacklem /* 402191783Srmacklem * Do enough of ifconfig(8) so that the critical net interface can 403191783Srmacklem * talk to the server. 404191783Srmacklem */ 405191783Srmacklem error = socreate(nd->myif.ifra_addr.sa_family, &so, nd->root_args.sotype, 0, 406191783Srmacklem td->td_ucred, td); 407191783Srmacklem if (error) 408192145Srmacklem panic("nfs_mountroot: socreate(%04x): %d", 409191783Srmacklem nd->myif.ifra_addr.sa_family, error); 410191783Srmacklem 411191783Srmacklem#if 0 /* XXX Bad idea */ 412191783Srmacklem /* 413191783Srmacklem * We might not have been told the right interface, so we pass 414191783Srmacklem * over the first ten interfaces of the same kind, until we get 415191783Srmacklem * one of them configured. 416191783Srmacklem */ 417191783Srmacklem 418191783Srmacklem for (i = strlen(nd->myif.ifra_name) - 1; 419191783Srmacklem nd->myif.ifra_name[i] >= '0' && 420191783Srmacklem nd->myif.ifra_name[i] <= '9'; 421191783Srmacklem nd->myif.ifra_name[i] ++) { 422191783Srmacklem error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td); 423191783Srmacklem if(!error) 424191783Srmacklem break; 425191783Srmacklem } 426191783Srmacklem#endif 427191783Srmacklem error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td); 428191783Srmacklem if (error) 429192145Srmacklem panic("nfs_mountroot: SIOCAIFADDR: %d", error); 430191783Srmacklem if ((cp = getenv("boot.netif.mtu")) != NULL) { 431191783Srmacklem ir.ifr_mtu = strtol(cp, NULL, 10); 432191783Srmacklem bcopy(nd->myif.ifra_name, ir.ifr_name, IFNAMSIZ); 433191783Srmacklem freeenv(cp); 434191783Srmacklem error = ifioctl(so, SIOCSIFMTU, (caddr_t)&ir, td); 435191783Srmacklem if (error) 436192145Srmacklem printf("nfs_mountroot: SIOCSIFMTU: %d", error); 437191783Srmacklem } 438191783Srmacklem soclose(so); 439191783Srmacklem 440191783Srmacklem /* 441191783Srmacklem * If the gateway field is filled in, set it as the default route. 442191783Srmacklem * Note that pxeboot will set a default route of 0 if the route 443191783Srmacklem * is not set by the DHCP server. Check also for a value of 0 444191783Srmacklem * to avoid panicking inappropriately in that situation. 445191783Srmacklem */ 446191783Srmacklem if (nd->mygateway.sin_len != 0 && 447191783Srmacklem nd->mygateway.sin_addr.s_addr != 0) { 448191783Srmacklem struct sockaddr_in mask, sin; 449191783Srmacklem 450191783Srmacklem bzero((caddr_t)&mask, sizeof(mask)); 451191783Srmacklem sin = mask; 452191783Srmacklem sin.sin_family = AF_INET; 453191783Srmacklem sin.sin_len = sizeof(sin); 454192145Srmacklem /* XXX MRT use table 0 for this sort of thing */ 455218757Sbz CURVNET_SET(TD_TO_VNET(td)); 456191783Srmacklem error = rtrequest(RTM_ADD, (struct sockaddr *)&sin, 457191783Srmacklem (struct sockaddr *)&nd->mygateway, 458191783Srmacklem (struct sockaddr *)&mask, 459191783Srmacklem RTF_UP | RTF_GATEWAY, NULL); 460218757Sbz CURVNET_RESTORE(); 461191783Srmacklem if (error) 462192145Srmacklem panic("nfs_mountroot: RTM_ADD: %d", error); 463191783Srmacklem } 464191783Srmacklem 465191783Srmacklem /* 466191783Srmacklem * Create the rootfs mount point. 467191783Srmacklem */ 468191783Srmacklem nd->root_args.fh = nd->root_fh; 469191783Srmacklem nd->root_args.fhsize = nd->root_fhsize; 470191783Srmacklem l = ntohl(nd->root_saddr.sin_addr.s_addr); 471191783Srmacklem snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld:%s", 472191783Srmacklem (l >> 24) & 0xff, (l >> 16) & 0xff, 473191783Srmacklem (l >> 8) & 0xff, (l >> 0) & 0xff, nd->root_hostnam); 474191783Srmacklem printf("NFS ROOT: %s\n", buf); 475192145Srmacklem nd->root_args.hostname = buf; 476191783Srmacklem if ((error = nfs_mountdiskless(buf, 477191783Srmacklem &nd->root_saddr, &nd->root_args, td, &vp, mp)) != 0) { 478191783Srmacklem return (error); 479191783Srmacklem } 480191783Srmacklem 481191783Srmacklem /* 482191783Srmacklem * This is not really an nfs issue, but it is much easier to 483191783Srmacklem * set hostname here and then let the "/etc/rc.xxx" files 484191783Srmacklem * mount the right /var based upon its preset value. 485191783Srmacklem */ 486193066Sjamie mtx_lock(&prison0.pr_mtx); 487194118Sjamie strlcpy(prison0.pr_hostname, nd->my_hostnam, 488194118Sjamie sizeof(prison0.pr_hostname)); 489193066Sjamie mtx_unlock(&prison0.pr_mtx); 490191783Srmacklem inittodr(ntohl(nd->root_time)); 491191783Srmacklem return (0); 492191783Srmacklem} 493191783Srmacklem 494191783Srmacklem/* 495191783Srmacklem * Internal version of mount system call for diskless setup. 496191783Srmacklem */ 497191783Srmacklemstatic int 498191783Srmacklemnfs_mountdiskless(char *path, 499191783Srmacklem struct sockaddr_in *sin, struct nfs_args *args, struct thread *td, 500191783Srmacklem struct vnode **vpp, struct mount *mp) 501191783Srmacklem{ 502191783Srmacklem struct sockaddr *nam; 503191783Srmacklem int error; 504191783Srmacklem 505191783Srmacklem nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK); 506191783Srmacklem if ((error = mountnfs(args, mp, nam, path, NULL, NULL, NULL, vpp, 507203303Srmacklem td->td_ucred, td, NFS_DEFAULT_NEGNAMETIMEO)) != 0) { 508192145Srmacklem printf("nfs_mountroot: mount %s on /: %d\n", path, error); 509191783Srmacklem return (error); 510191783Srmacklem } 511191783Srmacklem return (0); 512191783Srmacklem} 513191783Srmacklem 514191783Srmacklemstatic void 515192585Srmacklemnfs_sec_name(char *sec, int *flagsp) 516192585Srmacklem{ 517192585Srmacklem if (!strcmp(sec, "krb5")) 518192585Srmacklem *flagsp |= NFSMNT_KERB; 519192585Srmacklem else if (!strcmp(sec, "krb5i")) 520192585Srmacklem *flagsp |= (NFSMNT_KERB | NFSMNT_INTEGRITY); 521192585Srmacklem else if (!strcmp(sec, "krb5p")) 522192585Srmacklem *flagsp |= (NFSMNT_KERB | NFSMNT_PRIVACY); 523192585Srmacklem} 524192585Srmacklem 525192585Srmacklemstatic void 526191783Srmacklemnfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp, 527214048Srmacklem const char *hostname, struct ucred *cred, struct thread *td) 528191783Srmacklem{ 529191783Srmacklem int s; 530191783Srmacklem int adjsock; 531214048Srmacklem char *p; 532191783Srmacklem 533191783Srmacklem s = splnet(); 534191783Srmacklem 535191783Srmacklem /* 536191783Srmacklem * Set read-only flag if requested; otherwise, clear it if this is 537191783Srmacklem * an update. If this is not an update, then either the read-only 538191783Srmacklem * flag is already clear, or this is a root mount and it was set 539191783Srmacklem * intentionally at some previous point. 540191783Srmacklem */ 541191783Srmacklem if (vfs_getopt(mp->mnt_optnew, "ro", NULL, NULL) == 0) { 542191783Srmacklem MNT_ILOCK(mp); 543191783Srmacklem mp->mnt_flag |= MNT_RDONLY; 544191783Srmacklem MNT_IUNLOCK(mp); 545191783Srmacklem } else if (mp->mnt_flag & MNT_UPDATE) { 546191783Srmacklem MNT_ILOCK(mp); 547191783Srmacklem mp->mnt_flag &= ~MNT_RDONLY; 548191783Srmacklem MNT_IUNLOCK(mp); 549191783Srmacklem } 550191783Srmacklem 551191783Srmacklem /* 552191783Srmacklem * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes 553191783Srmacklem * no sense in that context. Also, set up appropriate retransmit 554191783Srmacklem * and soft timeout behavior. 555191783Srmacklem */ 556191783Srmacklem if (argp->sotype == SOCK_STREAM) { 557191783Srmacklem nmp->nm_flag &= ~NFSMNT_NOCONN; 558191783Srmacklem nmp->nm_timeo = NFS_MAXTIMEO; 559191783Srmacklem } 560191783Srmacklem 561191783Srmacklem /* Also clear RDIRPLUS if not NFSv3, it crashes some servers */ 562191783Srmacklem if ((argp->flags & NFSMNT_NFSV3) == 0) 563191783Srmacklem nmp->nm_flag &= ~NFSMNT_RDIRPLUS; 564191783Srmacklem 565191783Srmacklem /* Also re-bind if we're switching to/from a connected UDP socket */ 566191783Srmacklem adjsock = ((nmp->nm_flag & NFSMNT_NOCONN) != 567191783Srmacklem (argp->flags & NFSMNT_NOCONN)); 568191783Srmacklem 569191783Srmacklem /* Update flags atomically. Don't change the lock bits. */ 570191783Srmacklem nmp->nm_flag = argp->flags | nmp->nm_flag; 571191783Srmacklem splx(s); 572191783Srmacklem 573191783Srmacklem if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) { 574191783Srmacklem nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10; 575191783Srmacklem if (nmp->nm_timeo < NFS_MINTIMEO) 576191783Srmacklem nmp->nm_timeo = NFS_MINTIMEO; 577191783Srmacklem else if (nmp->nm_timeo > NFS_MAXTIMEO) 578191783Srmacklem nmp->nm_timeo = NFS_MAXTIMEO; 579191783Srmacklem } 580191783Srmacklem 581191783Srmacklem if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) { 582191783Srmacklem nmp->nm_retry = argp->retrans; 583191783Srmacklem if (nmp->nm_retry > NFS_MAXREXMIT) 584191783Srmacklem nmp->nm_retry = NFS_MAXREXMIT; 585191783Srmacklem } 586191783Srmacklem 587191783Srmacklem if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) { 588191783Srmacklem nmp->nm_wsize = argp->wsize; 589191783Srmacklem /* Round down to multiple of blocksize */ 590191783Srmacklem nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1); 591191783Srmacklem if (nmp->nm_wsize <= 0) 592191783Srmacklem nmp->nm_wsize = NFS_FABLKSIZE; 593191783Srmacklem } 594191783Srmacklem 595191783Srmacklem if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) { 596191783Srmacklem nmp->nm_rsize = argp->rsize; 597191783Srmacklem /* Round down to multiple of blocksize */ 598191783Srmacklem nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1); 599191783Srmacklem if (nmp->nm_rsize <= 0) 600191783Srmacklem nmp->nm_rsize = NFS_FABLKSIZE; 601191783Srmacklem } 602191783Srmacklem 603191783Srmacklem if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) { 604191783Srmacklem nmp->nm_readdirsize = argp->readdirsize; 605191783Srmacklem } 606191783Srmacklem 607191783Srmacklem if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0) 608191783Srmacklem nmp->nm_acregmin = argp->acregmin; 609191783Srmacklem else 610191783Srmacklem nmp->nm_acregmin = NFS_MINATTRTIMO; 611191783Srmacklem if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0) 612191783Srmacklem nmp->nm_acregmax = argp->acregmax; 613191783Srmacklem else 614191783Srmacklem nmp->nm_acregmax = NFS_MAXATTRTIMO; 615191783Srmacklem if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0) 616191783Srmacklem nmp->nm_acdirmin = argp->acdirmin; 617191783Srmacklem else 618191783Srmacklem nmp->nm_acdirmin = NFS_MINDIRATTRTIMO; 619191783Srmacklem if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0) 620191783Srmacklem nmp->nm_acdirmax = argp->acdirmax; 621191783Srmacklem else 622191783Srmacklem nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO; 623191783Srmacklem if (nmp->nm_acdirmin > nmp->nm_acdirmax) 624191783Srmacklem nmp->nm_acdirmin = nmp->nm_acdirmax; 625191783Srmacklem if (nmp->nm_acregmin > nmp->nm_acregmax) 626191783Srmacklem nmp->nm_acregmin = nmp->nm_acregmax; 627191783Srmacklem 628191783Srmacklem if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) { 629191783Srmacklem if (argp->readahead <= NFS_MAXRAHEAD) 630191783Srmacklem nmp->nm_readahead = argp->readahead; 631191783Srmacklem else 632191783Srmacklem nmp->nm_readahead = NFS_MAXRAHEAD; 633191783Srmacklem } 634191783Srmacklem if ((argp->flags & NFSMNT_WCOMMITSIZE) && argp->wcommitsize >= 0) { 635191783Srmacklem if (argp->wcommitsize < nmp->nm_wsize) 636191783Srmacklem nmp->nm_wcommitsize = nmp->nm_wsize; 637191783Srmacklem else 638191783Srmacklem nmp->nm_wcommitsize = argp->wcommitsize; 639191783Srmacklem } 640191783Srmacklem 641191783Srmacklem adjsock |= ((nmp->nm_sotype != argp->sotype) || 642191783Srmacklem (nmp->nm_soproto != argp->proto)); 643191783Srmacklem 644191783Srmacklem if (nmp->nm_client != NULL && adjsock) { 645191783Srmacklem int haslock = 0, error = 0; 646191783Srmacklem 647191783Srmacklem if (nmp->nm_sotype == SOCK_STREAM) { 648191783Srmacklem error = newnfs_sndlock(&nmp->nm_sockreq.nr_lock); 649191783Srmacklem if (!error) 650191783Srmacklem haslock = 1; 651191783Srmacklem } 652191783Srmacklem if (!error) { 653191783Srmacklem newnfs_disconnect(&nmp->nm_sockreq); 654191783Srmacklem if (haslock) 655191783Srmacklem newnfs_sndunlock(&nmp->nm_sockreq.nr_lock); 656191783Srmacklem nmp->nm_sotype = argp->sotype; 657191783Srmacklem nmp->nm_soproto = argp->proto; 658191783Srmacklem if (nmp->nm_sotype == SOCK_DGRAM) 659191783Srmacklem while (newnfs_connect(nmp, &nmp->nm_sockreq, 660191783Srmacklem cred, td, 0)) { 661191783Srmacklem printf("newnfs_args: retrying connect\n"); 662207170Srmacklem (void) nfs_catnap(PSOCK, 0, "newnfscon"); 663191783Srmacklem } 664191783Srmacklem } 665191783Srmacklem } else { 666191783Srmacklem nmp->nm_sotype = argp->sotype; 667191783Srmacklem nmp->nm_soproto = argp->proto; 668191783Srmacklem } 669214048Srmacklem 670214048Srmacklem if (hostname != NULL) { 671214048Srmacklem strlcpy(nmp->nm_hostname, hostname, 672214048Srmacklem sizeof(nmp->nm_hostname)); 673214048Srmacklem p = strchr(nmp->nm_hostname, ':'); 674214048Srmacklem if (p != NULL) 675214048Srmacklem *p = '\0'; 676214048Srmacklem } 677191783Srmacklem} 678191783Srmacklem 679192585Srmacklemstatic const char *nfs_opts[] = { "from", 680191783Srmacklem "noatime", "noexec", "suiddir", "nosuid", "nosymfollow", "union", 681191783Srmacklem "noclusterr", "noclusterw", "multilabel", "acls", "force", "update", 682192585Srmacklem "async", "noconn", "nolockd", "conn", "lockd", "intr", "rdirplus", 683192585Srmacklem "readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize", 684192585Srmacklem "retrans", "acregmin", "acregmax", "acdirmin", "acdirmax", "resvport", 685192585Srmacklem "readahead", "hostname", "timeout", "addr", "fh", "nfsv3", "sec", 686192585Srmacklem "principal", "nfsv4", "gssname", "allgssname", "dirpath", 687203303Srmacklem "negnametimeo", 688191783Srmacklem NULL }; 689191783Srmacklem 690191783Srmacklem/* 691191783Srmacklem * VFS Operations. 692191783Srmacklem * 693191783Srmacklem * mount system call 694191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then 695191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before 696191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and 697191783Srmacklem * an error after that means that I have to release the mbuf. 698191783Srmacklem */ 699191783Srmacklem/* ARGSUSED */ 700191783Srmacklemstatic int 701191990Sattilionfs_mount(struct mount *mp) 702191783Srmacklem{ 703191783Srmacklem struct nfs_args args = { 704191783Srmacklem .version = NFS_ARGSVERSION, 705191783Srmacklem .addr = NULL, 706191783Srmacklem .addrlen = sizeof (struct sockaddr_in), 707191783Srmacklem .sotype = SOCK_STREAM, 708191783Srmacklem .proto = 0, 709191783Srmacklem .fh = NULL, 710191783Srmacklem .fhsize = 0, 711191783Srmacklem .flags = 0, 712191783Srmacklem .wsize = NFS_WSIZE, 713191783Srmacklem .rsize = NFS_RSIZE, 714191783Srmacklem .readdirsize = NFS_READDIRSIZE, 715191783Srmacklem .timeo = 10, 716191783Srmacklem .retrans = NFS_RETRANS, 717191783Srmacklem .readahead = NFS_DEFRAHEAD, 718191783Srmacklem .wcommitsize = 0, /* was: NQ_DEFLEASE */ 719191783Srmacklem .hostname = NULL, 720191783Srmacklem /* args version 4 */ 721191783Srmacklem .acregmin = NFS_MINATTRTIMO, 722191783Srmacklem .acregmax = NFS_MAXATTRTIMO, 723191783Srmacklem .acdirmin = NFS_MINDIRATTRTIMO, 724191783Srmacklem .acdirmax = NFS_MAXDIRATTRTIMO, 725191783Srmacklem .dirlen = 0, 726191783Srmacklem .krbnamelen = 0, 727192585Srmacklem .srvkrbnamelen = 0, 728191783Srmacklem }; 729192585Srmacklem int error = 0, ret, len; 730192585Srmacklem struct sockaddr *nam = NULL; 731191783Srmacklem struct vnode *vp; 732191990Sattilio struct thread *td; 733191783Srmacklem char hst[MNAMELEN]; 734191783Srmacklem u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100]; 735192585Srmacklem char *opt, *name, *secname; 736203303Srmacklem int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO; 737191783Srmacklem 738191783Srmacklem if (vfs_filteropt(mp->mnt_optnew, nfs_opts)) { 739191783Srmacklem error = EINVAL; 740191783Srmacklem goto out; 741191783Srmacklem } 742191783Srmacklem 743191990Sattilio td = curthread; 744191783Srmacklem if ((mp->mnt_flag & (MNT_ROOTFS | MNT_UPDATE)) == MNT_ROOTFS) { 745192145Srmacklem error = ncl_mountroot(mp); 746191783Srmacklem goto out; 747191783Srmacklem } 748191783Srmacklem 749192585Srmacklem nfscl_init(); 750191783Srmacklem 751192585Srmacklem /* Handle the new style options. */ 752192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "noconn", NULL, NULL) == 0) 753192585Srmacklem args.flags |= NFSMNT_NOCONN; 754192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "conn", NULL, NULL) == 0) 755192585Srmacklem args.flags |= NFSMNT_NOCONN; 756192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "nolockd", NULL, NULL) == 0) 757192585Srmacklem args.flags |= NFSMNT_NOLOCKD; 758192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "lockd", NULL, NULL) == 0) 759192585Srmacklem args.flags &= ~NFSMNT_NOLOCKD; 760192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "intr", NULL, NULL) == 0) 761192585Srmacklem args.flags |= NFSMNT_INT; 762192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "rdirplus", NULL, NULL) == 0) 763192585Srmacklem args.flags |= NFSMNT_RDIRPLUS; 764192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "resvport", NULL, NULL) == 0) 765192585Srmacklem args.flags |= NFSMNT_RESVPORT; 766192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "noresvport", NULL, NULL) == 0) 767192585Srmacklem args.flags &= ~NFSMNT_RESVPORT; 768192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "soft", NULL, NULL) == 0) 769192585Srmacklem args.flags |= NFSMNT_SOFT; 770192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "hard", NULL, NULL) == 0) 771192585Srmacklem args.flags &= ~NFSMNT_SOFT; 772192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "mntudp", NULL, NULL) == 0) 773192585Srmacklem args.sotype = SOCK_DGRAM; 774192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "udp", NULL, NULL) == 0) 775192585Srmacklem args.sotype = SOCK_DGRAM; 776192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "tcp", NULL, NULL) == 0) 777192585Srmacklem args.sotype = SOCK_STREAM; 778192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "nfsv3", NULL, NULL) == 0) 779192585Srmacklem args.flags |= NFSMNT_NFSV3; 780192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "nfsv4", NULL, NULL) == 0) { 781192585Srmacklem args.flags |= NFSMNT_NFSV4; 782192585Srmacklem args.sotype = SOCK_STREAM; 783191783Srmacklem } 784192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "allgssname", NULL, NULL) == 0) 785192585Srmacklem args.flags |= NFSMNT_ALLGSSNAME; 786192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) { 787192585Srmacklem if (opt == NULL) { 788192585Srmacklem vfs_mount_error(mp, "illegal readdirsize"); 789192585Srmacklem error = EINVAL; 790192585Srmacklem goto out; 791192585Srmacklem } 792192585Srmacklem ret = sscanf(opt, "%d", &args.readdirsize); 793192585Srmacklem if (ret != 1 || args.readdirsize <= 0) { 794192585Srmacklem vfs_mount_error(mp, "illegal readdirsize: %s", 795192585Srmacklem opt); 796192585Srmacklem error = EINVAL; 797192585Srmacklem goto out; 798192585Srmacklem } 799192585Srmacklem args.flags |= NFSMNT_READDIRSIZE; 800192585Srmacklem } 801192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "readahead", (void **)&opt, NULL) == 0) { 802192585Srmacklem if (opt == NULL) { 803192585Srmacklem vfs_mount_error(mp, "illegal readahead"); 804192585Srmacklem error = EINVAL; 805192585Srmacklem goto out; 806192585Srmacklem } 807192585Srmacklem ret = sscanf(opt, "%d", &args.readahead); 808192585Srmacklem if (ret != 1 || args.readahead <= 0) { 809192585Srmacklem vfs_mount_error(mp, "illegal readahead: %s", 810192585Srmacklem opt); 811192585Srmacklem error = EINVAL; 812192585Srmacklem goto out; 813192585Srmacklem } 814192585Srmacklem args.flags |= NFSMNT_READAHEAD; 815192585Srmacklem } 816192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "wsize", (void **)&opt, NULL) == 0) { 817192585Srmacklem if (opt == NULL) { 818192585Srmacklem vfs_mount_error(mp, "illegal wsize"); 819192585Srmacklem error = EINVAL; 820192585Srmacklem goto out; 821192585Srmacklem } 822192585Srmacklem ret = sscanf(opt, "%d", &args.wsize); 823192585Srmacklem if (ret != 1 || args.wsize <= 0) { 824192585Srmacklem vfs_mount_error(mp, "illegal wsize: %s", 825192585Srmacklem opt); 826192585Srmacklem error = EINVAL; 827192585Srmacklem goto out; 828192585Srmacklem } 829192585Srmacklem args.flags |= NFSMNT_WSIZE; 830192585Srmacklem } 831192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "rsize", (void **)&opt, NULL) == 0) { 832192585Srmacklem if (opt == NULL) { 833192585Srmacklem vfs_mount_error(mp, "illegal rsize"); 834192585Srmacklem error = EINVAL; 835192585Srmacklem goto out; 836192585Srmacklem } 837192585Srmacklem ret = sscanf(opt, "%d", &args.rsize); 838192585Srmacklem if (ret != 1 || args.rsize <= 0) { 839192585Srmacklem vfs_mount_error(mp, "illegal wsize: %s", 840192585Srmacklem opt); 841192585Srmacklem error = EINVAL; 842192585Srmacklem goto out; 843192585Srmacklem } 844192585Srmacklem args.flags |= NFSMNT_RSIZE; 845192585Srmacklem } 846192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "retrans", (void **)&opt, NULL) == 0) { 847192585Srmacklem if (opt == NULL) { 848192585Srmacklem vfs_mount_error(mp, "illegal retrans"); 849192585Srmacklem error = EINVAL; 850192585Srmacklem goto out; 851192585Srmacklem } 852192585Srmacklem ret = sscanf(opt, "%d", &args.retrans); 853192585Srmacklem if (ret != 1 || args.retrans <= 0) { 854192585Srmacklem vfs_mount_error(mp, "illegal retrans: %s", 855192585Srmacklem opt); 856192585Srmacklem error = EINVAL; 857192585Srmacklem goto out; 858192585Srmacklem } 859192585Srmacklem args.flags |= NFSMNT_RETRANS; 860192585Srmacklem } 861192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "acregmin", (void **)&opt, NULL) == 0) { 862192585Srmacklem ret = sscanf(opt, "%d", &args.acregmin); 863192585Srmacklem if (ret != 1 || args.acregmin < 0) { 864192585Srmacklem vfs_mount_error(mp, "illegal acregmin: %s", 865192585Srmacklem opt); 866192585Srmacklem error = EINVAL; 867192585Srmacklem goto out; 868192585Srmacklem } 869192585Srmacklem args.flags |= NFSMNT_ACREGMIN; 870192585Srmacklem } 871192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "acregmax", (void **)&opt, NULL) == 0) { 872192585Srmacklem ret = sscanf(opt, "%d", &args.acregmax); 873192585Srmacklem if (ret != 1 || args.acregmax < 0) { 874192585Srmacklem vfs_mount_error(mp, "illegal acregmax: %s", 875192585Srmacklem opt); 876192585Srmacklem error = EINVAL; 877192585Srmacklem goto out; 878192585Srmacklem } 879192585Srmacklem args.flags |= NFSMNT_ACREGMAX; 880192585Srmacklem } 881192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "acdirmin", (void **)&opt, NULL) == 0) { 882192585Srmacklem ret = sscanf(opt, "%d", &args.acdirmin); 883192585Srmacklem if (ret != 1 || args.acdirmin < 0) { 884192585Srmacklem vfs_mount_error(mp, "illegal acdirmin: %s", 885192585Srmacklem opt); 886192585Srmacklem error = EINVAL; 887192585Srmacklem goto out; 888192585Srmacklem } 889192585Srmacklem args.flags |= NFSMNT_ACDIRMIN; 890192585Srmacklem } 891192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "acdirmax", (void **)&opt, NULL) == 0) { 892192585Srmacklem ret = sscanf(opt, "%d", &args.acdirmax); 893192585Srmacklem if (ret != 1 || args.acdirmax < 0) { 894192585Srmacklem vfs_mount_error(mp, "illegal acdirmax: %s", 895192585Srmacklem opt); 896192585Srmacklem error = EINVAL; 897192585Srmacklem goto out; 898192585Srmacklem } 899192585Srmacklem args.flags |= NFSMNT_ACDIRMAX; 900192585Srmacklem } 901192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "timeout", (void **)&opt, NULL) == 0) { 902192585Srmacklem ret = sscanf(opt, "%d", &args.timeo); 903192585Srmacklem if (ret != 1 || args.timeo <= 0) { 904192585Srmacklem vfs_mount_error(mp, "illegal timeout: %s", 905192585Srmacklem opt); 906192585Srmacklem error = EINVAL; 907192585Srmacklem goto out; 908192585Srmacklem } 909192585Srmacklem args.flags |= NFSMNT_TIMEO; 910192585Srmacklem } 911203303Srmacklem if (vfs_getopt(mp->mnt_optnew, "negnametimeo", (void **)&opt, NULL) 912203303Srmacklem == 0) { 913203303Srmacklem ret = sscanf(opt, "%d", &negnametimeo); 914203303Srmacklem if (ret != 1 || negnametimeo < 0) { 915203303Srmacklem vfs_mount_error(mp, "illegal negnametimeo: %s", 916203303Srmacklem opt); 917203303Srmacklem error = EINVAL; 918203303Srmacklem goto out; 919203303Srmacklem } 920203303Srmacklem } 921192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "sec", 922192585Srmacklem (void **) &secname, NULL) == 0) 923192585Srmacklem nfs_sec_name(secname, &args.flags); 924191783Srmacklem 925191783Srmacklem if (mp->mnt_flag & MNT_UPDATE) { 926191783Srmacklem struct nfsmount *nmp = VFSTONFS(mp); 927191783Srmacklem 928191783Srmacklem if (nmp == NULL) { 929191783Srmacklem error = EIO; 930191783Srmacklem goto out; 931191783Srmacklem } 932191783Srmacklem /* 933191783Srmacklem * When doing an update, we can't change version, 934191783Srmacklem * security, switch lockd strategies or change cookie 935191783Srmacklem * translation 936191783Srmacklem */ 937191783Srmacklem args.flags = (args.flags & 938191783Srmacklem ~(NFSMNT_NFSV3 | 939191783Srmacklem NFSMNT_NFSV4 | 940191783Srmacklem NFSMNT_KERB | 941191783Srmacklem NFSMNT_INTEGRITY | 942191783Srmacklem NFSMNT_PRIVACY | 943191783Srmacklem NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)) | 944191783Srmacklem (nmp->nm_flag & 945191783Srmacklem (NFSMNT_NFSV3 | 946191783Srmacklem NFSMNT_NFSV4 | 947191783Srmacklem NFSMNT_KERB | 948191783Srmacklem NFSMNT_INTEGRITY | 949191783Srmacklem NFSMNT_PRIVACY | 950191783Srmacklem NFSMNT_NOLOCKD /*|NFSMNT_XLATECOOKIE*/)); 951214048Srmacklem nfs_decode_args(mp, nmp, &args, NULL, td->td_ucred, td); 952191783Srmacklem goto out; 953191783Srmacklem } 954191783Srmacklem 955191783Srmacklem /* 956191783Srmacklem * Make the nfs_ip_paranoia sysctl serve as the default connection 957191783Srmacklem * or no-connection mode for those protocols that support 958191783Srmacklem * no-connection mode (the flag will be cleared later for protocols 959191783Srmacklem * that do not support no-connection mode). This will allow a client 960191783Srmacklem * to receive replies from a different IP then the request was 961191783Srmacklem * sent to. Note: default value for nfs_ip_paranoia is 1 (paranoid), 962191783Srmacklem * not 0. 963191783Srmacklem */ 964191783Srmacklem if (nfs_ip_paranoia == 0) 965191783Srmacklem args.flags |= NFSMNT_NOCONN; 966192585Srmacklem 967192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "fh", (void **)&args.fh, 968192585Srmacklem &args.fhsize) == 0) { 969208234Srmacklem if (args.fhsize < 0 || args.fhsize > NFSX_FHMAX) { 970192585Srmacklem vfs_mount_error(mp, "Bad file handle"); 971191783Srmacklem error = EINVAL; 972191783Srmacklem goto out; 973191783Srmacklem } 974192585Srmacklem bcopy(args.fh, nfh, args.fhsize); 975191783Srmacklem } else { 976192585Srmacklem args.fhsize = 0; 977192585Srmacklem } 978192585Srmacklem 979192585Srmacklem (void) vfs_getopt(mp->mnt_optnew, "hostname", (void **)&args.hostname, 980192585Srmacklem &len); 981192585Srmacklem if (args.hostname == NULL) { 982192585Srmacklem vfs_mount_error(mp, "Invalid hostname"); 983192585Srmacklem error = EINVAL; 984192585Srmacklem goto out; 985192585Srmacklem } 986192585Srmacklem bcopy(args.hostname, hst, MNAMELEN); 987192585Srmacklem hst[MNAMELEN - 1] = '\0'; 988192585Srmacklem 989192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "principal", (void **)&name, NULL) == 0) 990192585Srmacklem strlcpy(srvkrbname, name, sizeof (srvkrbname)); 991192585Srmacklem else 992192585Srmacklem snprintf(srvkrbname, sizeof (srvkrbname), "nfs@%s", hst); 993192585Srmacklem args.srvkrbnamelen = strlen(srvkrbname); 994192585Srmacklem 995192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "gssname", (void **)&name, NULL) == 0) 996192585Srmacklem strlcpy(krbname, name, sizeof (krbname)); 997192585Srmacklem else 998191783Srmacklem krbname[0] = '\0'; 999192585Srmacklem args.krbnamelen = strlen(krbname); 1000192585Srmacklem 1001192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "dirpath", (void **)&name, NULL) == 0) 1002192585Srmacklem strlcpy(dirpath, name, sizeof (dirpath)); 1003192585Srmacklem else 1004191783Srmacklem dirpath[0] = '\0'; 1005192585Srmacklem args.dirlen = strlen(dirpath); 1006192585Srmacklem 1007192585Srmacklem if (vfs_getopt(mp->mnt_optnew, "addr", (void **)&args.addr, 1008192585Srmacklem &args.addrlen) == 0) { 1009192585Srmacklem if (args.addrlen > SOCK_MAXADDRLEN) { 1010192585Srmacklem error = ENAMETOOLONG; 1011191783Srmacklem goto out; 1012191783Srmacklem } 1013192585Srmacklem nam = malloc(args.addrlen, M_SONAME, M_WAITOK); 1014192585Srmacklem bcopy(args.addr, nam, args.addrlen); 1015192585Srmacklem nam->sa_len = args.addrlen; 1016191783Srmacklem } 1017192585Srmacklem 1018191783Srmacklem args.fh = nfh; 1019191783Srmacklem error = mountnfs(&args, mp, nam, hst, krbname, dirpath, srvkrbname, 1020203303Srmacklem &vp, td->td_ucred, td, negnametimeo); 1021191783Srmacklemout: 1022191783Srmacklem if (!error) { 1023191783Srmacklem MNT_ILOCK(mp); 1024191783Srmacklem mp->mnt_kern_flag |= (MNTK_MPSAFE|MNTK_LOOKUP_SHARED); 1025191783Srmacklem MNT_IUNLOCK(mp); 1026191783Srmacklem } 1027191783Srmacklem return (error); 1028191783Srmacklem} 1029191783Srmacklem 1030191783Srmacklem 1031191783Srmacklem/* 1032191783Srmacklem * VFS Operations. 1033191783Srmacklem * 1034191783Srmacklem * mount system call 1035191783Srmacklem * It seems a bit dumb to copyinstr() the host and path here and then 1036191783Srmacklem * bcopy() them in mountnfs(), but I wanted to detect errors before 1037191783Srmacklem * doing the sockargs() call because sockargs() allocates an mbuf and 1038191783Srmacklem * an error after that means that I have to release the mbuf. 1039191783Srmacklem */ 1040191783Srmacklem/* ARGSUSED */ 1041191783Srmacklemstatic int 1042191990Sattilionfs_cmount(struct mntarg *ma, void *data, int flags) 1043191783Srmacklem{ 1044191783Srmacklem int error; 1045191783Srmacklem struct nfs_args args; 1046191783Srmacklem 1047191783Srmacklem error = copyin(data, &args, sizeof (struct nfs_args)); 1048191783Srmacklem if (error) 1049191783Srmacklem return error; 1050191783Srmacklem 1051191783Srmacklem ma = mount_arg(ma, "nfs_args", &args, sizeof args); 1052191783Srmacklem 1053191783Srmacklem error = kernel_mount(ma, flags); 1054191783Srmacklem return (error); 1055191783Srmacklem} 1056191783Srmacklem 1057191783Srmacklem/* 1058191783Srmacklem * Common code for mount and mountroot 1059191783Srmacklem */ 1060191783Srmacklemstatic int 1061191783Srmacklemmountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam, 1062191783Srmacklem char *hst, u_char *krbname, u_char *dirpath, u_char *srvkrbname, 1063203303Srmacklem struct vnode **vpp, struct ucred *cred, struct thread *td, 1064203303Srmacklem int negnametimeo) 1065191783Srmacklem{ 1066191783Srmacklem struct nfsmount *nmp; 1067191783Srmacklem struct nfsnode *np; 1068195762Srmacklem int error, trycnt, ret; 1069191783Srmacklem struct nfsvattr nfsva; 1070191783Srmacklem static u_int64_t clval = 0; 1071191783Srmacklem 1072191783Srmacklem if (mp->mnt_flag & MNT_UPDATE) { 1073191783Srmacklem nmp = VFSTONFS(mp); 1074191783Srmacklem printf("%s: MNT_UPDATE is no longer handled here\n", __func__); 1075191783Srmacklem FREE(nam, M_SONAME); 1076191783Srmacklem return (0); 1077191783Srmacklem } else { 1078191783Srmacklem MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount) + 1079191783Srmacklem argp->krbnamelen + argp->dirlen + argp->srvkrbnamelen + 2, 1080191783Srmacklem M_NEWNFSMNT, M_WAITOK); 1081191783Srmacklem bzero((caddr_t)nmp, sizeof (struct nfsmount) + 1082191783Srmacklem argp->krbnamelen + argp->dirlen + argp->srvkrbnamelen + 2); 1083191783Srmacklem TAILQ_INIT(&nmp->nm_bufq); 1084191783Srmacklem if (clval == 0) 1085191783Srmacklem clval = (u_int64_t)nfsboottime.tv_sec; 1086191783Srmacklem nmp->nm_clval = clval++; 1087191783Srmacklem nmp->nm_krbnamelen = argp->krbnamelen; 1088191783Srmacklem nmp->nm_dirpathlen = argp->dirlen; 1089191783Srmacklem nmp->nm_srvkrbnamelen = argp->srvkrbnamelen; 1090192675Srmacklem if (td->td_ucred->cr_uid != (uid_t)0) { 1091191783Srmacklem /* 1092192675Srmacklem * nm_uid is used to get KerberosV credentials for 1093192675Srmacklem * the nfsv4 state handling operations if there is 1094192675Srmacklem * no host based principal set. Use the uid of 1095192675Srmacklem * this user if not root, since they are doing the 1096192675Srmacklem * mount. I don't think setting this for root will 1097192675Srmacklem * work, since root normally does not have user 1098192675Srmacklem * credentials in a credentials cache. 1099191783Srmacklem */ 1100192675Srmacklem nmp->nm_uid = td->td_ucred->cr_uid; 1101191783Srmacklem } else { 1102191783Srmacklem /* 1103192675Srmacklem * Just set to -1, so it won't be used. 1104191783Srmacklem */ 1105191783Srmacklem nmp->nm_uid = (uid_t)-1; 1106191783Srmacklem } 1107191783Srmacklem 1108191783Srmacklem /* Copy and null terminate all the names */ 1109191783Srmacklem if (nmp->nm_krbnamelen > 0) { 1110191783Srmacklem bcopy(krbname, nmp->nm_krbname, nmp->nm_krbnamelen); 1111191783Srmacklem nmp->nm_name[nmp->nm_krbnamelen] = '\0'; 1112191783Srmacklem } 1113191783Srmacklem if (nmp->nm_dirpathlen > 0) { 1114191783Srmacklem bcopy(dirpath, NFSMNT_DIRPATH(nmp), 1115191783Srmacklem nmp->nm_dirpathlen); 1116191783Srmacklem nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen 1117191783Srmacklem + 1] = '\0'; 1118191783Srmacklem } 1119191783Srmacklem if (nmp->nm_srvkrbnamelen > 0) { 1120191783Srmacklem bcopy(srvkrbname, NFSMNT_SRVKRBNAME(nmp), 1121191783Srmacklem nmp->nm_srvkrbnamelen); 1122191783Srmacklem nmp->nm_name[nmp->nm_krbnamelen + nmp->nm_dirpathlen 1123191783Srmacklem + nmp->nm_srvkrbnamelen + 2] = '\0'; 1124191783Srmacklem } 1125191783Srmacklem nmp->nm_sockreq.nr_cred = crhold(cred); 1126191783Srmacklem mtx_init(&nmp->nm_sockreq.nr_mtx, "nfssock", NULL, MTX_DEF); 1127191783Srmacklem mp->mnt_data = nmp; 1128214048Srmacklem nmp->nm_getinfo = nfs_getnlminfo; 1129216931Srmacklem nmp->nm_vinvalbuf = ncl_vinvalbuf; 1130191783Srmacklem } 1131191783Srmacklem vfs_getnewfsid(mp); 1132191783Srmacklem nmp->nm_mountp = mp; 1133191783Srmacklem mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK); 1134203303Srmacklem nmp->nm_negnametimeo = negnametimeo; 1135191783Srmacklem 1136214048Srmacklem nfs_decode_args(mp, nmp, argp, hst, cred, td); 1137192585Srmacklem 1138191783Srmacklem /* 1139191783Srmacklem * V2 can only handle 32 bit filesizes. A 4GB-1 limit may be too 1140191783Srmacklem * high, depending on whether we end up with negative offsets in 1141191783Srmacklem * the client or server somewhere. 2GB-1 may be safer. 1142191783Srmacklem * 1143191783Srmacklem * For V3, ncl_fsinfo will adjust this as necessary. Assume maximum 1144191783Srmacklem * that we can handle until we find out otherwise. 1145191783Srmacklem * XXX Our "safe" limit on the client is what we can store in our 1146191783Srmacklem * buffer cache using signed(!) block numbers. 1147191783Srmacklem */ 1148191783Srmacklem if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) 1149191783Srmacklem nmp->nm_maxfilesize = 0xffffffffLL; 1150191783Srmacklem else 1151191783Srmacklem nmp->nm_maxfilesize = (u_int64_t)0x80000000 * DEV_BSIZE - 1; 1152191783Srmacklem 1153191783Srmacklem nmp->nm_timeo = NFS_TIMEO; 1154191783Srmacklem nmp->nm_retry = NFS_RETRANS; 1155191783Srmacklem if ((argp->flags & (NFSMNT_NFSV3 | NFSMNT_NFSV4)) == 0) { 1156191783Srmacklem nmp->nm_wsize = NFS_WSIZE; 1157191783Srmacklem nmp->nm_rsize = NFS_RSIZE; 1158191783Srmacklem nmp->nm_readdirsize = NFS_READDIRSIZE; 1159191783Srmacklem } 1160191783Srmacklem nmp->nm_wcommitsize = hibufspace / (desiredvnodes / 1000); 1161191783Srmacklem nmp->nm_numgrps = NFS_MAXGRPS; 1162191783Srmacklem nmp->nm_readahead = NFS_DEFRAHEAD; 1163191783Srmacklem nmp->nm_tprintf_delay = nfs_tprintf_delay; 1164191783Srmacklem if (nmp->nm_tprintf_delay < 0) 1165191783Srmacklem nmp->nm_tprintf_delay = 0; 1166191783Srmacklem nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay; 1167191783Srmacklem if (nmp->nm_tprintf_initial_delay < 0) 1168191783Srmacklem nmp->nm_tprintf_initial_delay = 0; 1169191783Srmacklem nmp->nm_fhsize = argp->fhsize; 1170191783Srmacklem if (nmp->nm_fhsize > 0) 1171191783Srmacklem bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize); 1172191783Srmacklem bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN); 1173191783Srmacklem nmp->nm_nam = nam; 1174191783Srmacklem /* Set up the sockets and per-host congestion */ 1175191783Srmacklem nmp->nm_sotype = argp->sotype; 1176191783Srmacklem nmp->nm_soproto = argp->proto; 1177191783Srmacklem nmp->nm_sockreq.nr_prog = NFS_PROG; 1178191783Srmacklem if ((argp->flags & NFSMNT_NFSV4)) 1179191783Srmacklem nmp->nm_sockreq.nr_vers = NFS_VER4; 1180191783Srmacklem else if ((argp->flags & NFSMNT_NFSV3)) 1181191783Srmacklem nmp->nm_sockreq.nr_vers = NFS_VER3; 1182191783Srmacklem else 1183191783Srmacklem nmp->nm_sockreq.nr_vers = NFS_VER2; 1184191783Srmacklem 1185191783Srmacklem 1186191783Srmacklem if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0))) 1187191783Srmacklem goto bad; 1188191783Srmacklem 1189191783Srmacklem /* 1190191783Srmacklem * A reference count is needed on the nfsnode representing the 1191191783Srmacklem * remote root. If this object is not persistent, then backward 1192191783Srmacklem * traversals of the mount point (i.e. "..") will not work if 1193191783Srmacklem * the nfsnode gets flushed out of the cache. Ufs does not have 1194191783Srmacklem * this problem, because one can identify root inodes by their 1195191783Srmacklem * number == ROOTINO (2). 1196191783Srmacklem */ 1197191783Srmacklem if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) && 1198191783Srmacklem nmp->nm_dirpathlen > 0) { 1199191783Srmacklem /* 1200191783Srmacklem * If the fhsize on the mount point == 0 for V4, the mount 1201191783Srmacklem * path needs to be looked up. 1202191783Srmacklem */ 1203191783Srmacklem trycnt = 3; 1204191783Srmacklem do { 1205191783Srmacklem error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp), 1206191783Srmacklem cred, td); 1207191783Srmacklem if (error) 1208207170Srmacklem (void) nfs_catnap(PZERO, error, "nfsgetdirp"); 1209191783Srmacklem } while (error && --trycnt > 0); 1210191783Srmacklem if (error) { 1211191783Srmacklem error = nfscl_maperr(td, error, (uid_t)0, (gid_t)0); 1212191783Srmacklem goto bad; 1213191783Srmacklem } 1214191783Srmacklem } 1215191783Srmacklem if (nmp->nm_fhsize > 0) { 1216195762Srmacklem /* 1217195762Srmacklem * Set f_iosize to NFS_DIRBLKSIZ so that bo_bsize gets set 1218195762Srmacklem * non-zero for the root vnode. f_iosize will be set correctly 1219195762Srmacklem * by nfs_statfs() before any I/O occurs. 1220195762Srmacklem */ 1221195762Srmacklem mp->mnt_stat.f_iosize = NFS_DIRBLKSIZ; 1222191783Srmacklem error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np); 1223191783Srmacklem if (error) 1224191783Srmacklem goto bad; 1225191783Srmacklem *vpp = NFSTOV(np); 1226191783Srmacklem 1227191783Srmacklem /* 1228191783Srmacklem * Get file attributes and transfer parameters for the 1229191783Srmacklem * mountpoint. This has the side effect of filling in 1230191783Srmacklem * (*vpp)->v_type with the correct value. 1231191783Srmacklem */ 1232191783Srmacklem ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1, 1233191783Srmacklem cred, td, &nfsva, NULL); 1234191783Srmacklem if (ret) { 1235191783Srmacklem /* 1236191783Srmacklem * Just set default values to get things going. 1237191783Srmacklem */ 1238191783Srmacklem NFSBZERO((caddr_t)&nfsva, sizeof (struct nfsvattr)); 1239191783Srmacklem nfsva.na_vattr.va_type = VDIR; 1240191783Srmacklem nfsva.na_vattr.va_mode = 0777; 1241191783Srmacklem nfsva.na_vattr.va_nlink = 100; 1242191783Srmacklem nfsva.na_vattr.va_uid = (uid_t)0; 1243191783Srmacklem nfsva.na_vattr.va_gid = (gid_t)0; 1244191783Srmacklem nfsva.na_vattr.va_fileid = 2; 1245191783Srmacklem nfsva.na_vattr.va_gen = 1; 1246191783Srmacklem nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE; 1247191783Srmacklem nfsva.na_vattr.va_size = 512 * 1024; 1248191783Srmacklem } 1249191783Srmacklem (void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1); 1250191783Srmacklem if (argp->flags & NFSMNT_NFSV3) 1251191783Srmacklem ncl_fsinfo(nmp, *vpp, cred, td); 1252191783Srmacklem 1253191783Srmacklem /* 1254191783Srmacklem * Lose the lock but keep the ref. 1255191783Srmacklem */ 1256191783Srmacklem VOP_UNLOCK(*vpp, 0); 1257191783Srmacklem return (0); 1258191783Srmacklem } 1259191783Srmacklem error = EIO; 1260191783Srmacklem 1261191783Srmacklembad: 1262191783Srmacklem newnfs_disconnect(&nmp->nm_sockreq); 1263191783Srmacklem crfree(nmp->nm_sockreq.nr_cred); 1264191783Srmacklem mtx_destroy(&nmp->nm_sockreq.nr_mtx); 1265191783Srmacklem mtx_destroy(&nmp->nm_mtx); 1266191783Srmacklem FREE(nmp, M_NEWNFSMNT); 1267191783Srmacklem FREE(nam, M_SONAME); 1268191783Srmacklem return (error); 1269191783Srmacklem} 1270191783Srmacklem 1271191783Srmacklem/* 1272191783Srmacklem * unmount system call 1273191783Srmacklem */ 1274191783Srmacklemstatic int 1275191990Sattilionfs_unmount(struct mount *mp, int mntflags) 1276191783Srmacklem{ 1277191990Sattilio struct thread *td; 1278191783Srmacklem struct nfsmount *nmp; 1279191783Srmacklem int error, flags = 0, trycnt = 0; 1280191783Srmacklem 1281191990Sattilio td = curthread; 1282191990Sattilio 1283191783Srmacklem if (mntflags & MNT_FORCE) 1284191783Srmacklem flags |= FORCECLOSE; 1285191783Srmacklem nmp = VFSTONFS(mp); 1286191783Srmacklem /* 1287191783Srmacklem * Goes something like this.. 1288191783Srmacklem * - Call vflush() to clear out vnodes for this filesystem 1289191783Srmacklem * - Close the socket 1290191783Srmacklem * - Free up the data structures 1291191783Srmacklem */ 1292191783Srmacklem /* In the forced case, cancel any outstanding requests. */ 1293191783Srmacklem if (mntflags & MNT_FORCE) { 1294191783Srmacklem error = newnfs_nmcancelreqs(nmp); 1295191783Srmacklem if (error) 1296191783Srmacklem goto out; 1297191783Srmacklem /* For a forced close, get rid of the renew thread now */ 1298191783Srmacklem nfscl_umount(nmp, td); 1299191783Srmacklem } 1300191783Srmacklem /* We hold 1 extra ref on the root vnode; see comment in mountnfs(). */ 1301191783Srmacklem do { 1302191783Srmacklem error = vflush(mp, 1, flags, td); 1303191783Srmacklem if ((mntflags & MNT_FORCE) && error != 0 && ++trycnt < 30) 1304207170Srmacklem (void) nfs_catnap(PSOCK, error, "newndm"); 1305191783Srmacklem } while ((mntflags & MNT_FORCE) && error != 0 && trycnt < 30); 1306191783Srmacklem if (error) 1307191783Srmacklem goto out; 1308191783Srmacklem 1309191783Srmacklem /* 1310191783Srmacklem * We are now committed to the unmount. 1311191783Srmacklem */ 1312191783Srmacklem if ((mntflags & MNT_FORCE) == 0) 1313191783Srmacklem nfscl_umount(nmp, td); 1314191783Srmacklem newnfs_disconnect(&nmp->nm_sockreq); 1315191783Srmacklem crfree(nmp->nm_sockreq.nr_cred); 1316191783Srmacklem FREE(nmp->nm_nam, M_SONAME); 1317191783Srmacklem 1318191783Srmacklem mtx_destroy(&nmp->nm_sockreq.nr_mtx); 1319191783Srmacklem mtx_destroy(&nmp->nm_mtx); 1320191783Srmacklem FREE(nmp, M_NEWNFSMNT); 1321191783Srmacklemout: 1322191783Srmacklem return (error); 1323191783Srmacklem} 1324191783Srmacklem 1325191783Srmacklem/* 1326191783Srmacklem * Return root of a filesystem 1327191783Srmacklem */ 1328191783Srmacklemstatic int 1329191990Sattilionfs_root(struct mount *mp, int flags, struct vnode **vpp) 1330191783Srmacklem{ 1331191783Srmacklem struct vnode *vp; 1332191783Srmacklem struct nfsmount *nmp; 1333191783Srmacklem struct nfsnode *np; 1334191783Srmacklem int error; 1335191783Srmacklem 1336191783Srmacklem nmp = VFSTONFS(mp); 1337191783Srmacklem error = ncl_nget(mp, nmp->nm_fh, nmp->nm_fhsize, &np); 1338191783Srmacklem if (error) 1339191783Srmacklem return error; 1340191783Srmacklem vp = NFSTOV(np); 1341191783Srmacklem /* 1342191783Srmacklem * Get transfer parameters and attributes for root vnode once. 1343191783Srmacklem */ 1344191783Srmacklem mtx_lock(&nmp->nm_mtx); 1345191783Srmacklem if (NFSHASNFSV3(nmp) && !NFSHASGOTFSINFO(nmp)) { 1346191783Srmacklem mtx_unlock(&nmp->nm_mtx); 1347191783Srmacklem ncl_fsinfo(nmp, vp, curthread->td_ucred, curthread); 1348191783Srmacklem } else 1349191783Srmacklem mtx_unlock(&nmp->nm_mtx); 1350191783Srmacklem if (vp->v_type == VNON) 1351191783Srmacklem vp->v_type = VDIR; 1352191783Srmacklem vp->v_vflag |= VV_ROOT; 1353191783Srmacklem *vpp = vp; 1354191783Srmacklem return (0); 1355191783Srmacklem} 1356191783Srmacklem 1357191783Srmacklem/* 1358191783Srmacklem * Flush out the buffer cache 1359191783Srmacklem */ 1360191783Srmacklem/* ARGSUSED */ 1361191783Srmacklemstatic int 1362191990Sattilionfs_sync(struct mount *mp, int waitfor) 1363191783Srmacklem{ 1364191783Srmacklem struct vnode *vp, *mvp; 1365191990Sattilio struct thread *td; 1366191783Srmacklem int error, allerror = 0; 1367191783Srmacklem 1368191990Sattilio td = curthread; 1369191990Sattilio 1370191783Srmacklem /* 1371191783Srmacklem * Force stale buffer cache information to be flushed. 1372191783Srmacklem */ 1373191783Srmacklem MNT_ILOCK(mp); 1374191783Srmacklemloop: 1375191783Srmacklem MNT_VNODE_FOREACH(vp, mp, mvp) { 1376191783Srmacklem VI_LOCK(vp); 1377191783Srmacklem MNT_IUNLOCK(mp); 1378191783Srmacklem /* XXX Racy bv_cnt check. */ 1379191783Srmacklem if (VOP_ISLOCKED(vp) || vp->v_bufobj.bo_dirty.bv_cnt == 0 || 1380191783Srmacklem waitfor == MNT_LAZY) { 1381191783Srmacklem VI_UNLOCK(vp); 1382191783Srmacklem MNT_ILOCK(mp); 1383191783Srmacklem continue; 1384191783Srmacklem } 1385191783Srmacklem if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) { 1386191783Srmacklem MNT_ILOCK(mp); 1387191783Srmacklem MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp); 1388191783Srmacklem goto loop; 1389191783Srmacklem } 1390191783Srmacklem error = VOP_FSYNC(vp, waitfor, td); 1391191783Srmacklem if (error) 1392191783Srmacklem allerror = error; 1393191783Srmacklem VOP_UNLOCK(vp, 0); 1394191783Srmacklem vrele(vp); 1395191783Srmacklem 1396191783Srmacklem MNT_ILOCK(mp); 1397191783Srmacklem } 1398191783Srmacklem MNT_IUNLOCK(mp); 1399191783Srmacklem return (allerror); 1400191783Srmacklem} 1401191783Srmacklem 1402191783Srmacklemstatic int 1403191783Srmacklemnfs_sysctl(struct mount *mp, fsctlop_t op, struct sysctl_req *req) 1404191783Srmacklem{ 1405191783Srmacklem struct nfsmount *nmp = VFSTONFS(mp); 1406191783Srmacklem struct vfsquery vq; 1407191783Srmacklem int error; 1408191783Srmacklem 1409191783Srmacklem bzero(&vq, sizeof(vq)); 1410191783Srmacklem switch (op) { 1411191783Srmacklem#if 0 1412191783Srmacklem case VFS_CTL_NOLOCKS: 1413191783Srmacklem val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0; 1414191783Srmacklem if (req->oldptr != NULL) { 1415191783Srmacklem error = SYSCTL_OUT(req, &val, sizeof(val)); 1416191783Srmacklem if (error) 1417191783Srmacklem return (error); 1418191783Srmacklem } 1419191783Srmacklem if (req->newptr != NULL) { 1420191783Srmacklem error = SYSCTL_IN(req, &val, sizeof(val)); 1421191783Srmacklem if (error) 1422191783Srmacklem return (error); 1423191783Srmacklem if (val) 1424191783Srmacklem nmp->nm_flag |= NFSMNT_NOLOCKS; 1425191783Srmacklem else 1426191783Srmacklem nmp->nm_flag &= ~NFSMNT_NOLOCKS; 1427191783Srmacklem } 1428191783Srmacklem break; 1429191783Srmacklem#endif 1430191783Srmacklem case VFS_CTL_QUERY: 1431191783Srmacklem mtx_lock(&nmp->nm_mtx); 1432191783Srmacklem if (nmp->nm_state & NFSSTA_TIMEO) 1433191783Srmacklem vq.vq_flags |= VQ_NOTRESP; 1434191783Srmacklem mtx_unlock(&nmp->nm_mtx); 1435191783Srmacklem#if 0 1436191783Srmacklem if (!(nmp->nm_flag & NFSMNT_NOLOCKS) && 1437191783Srmacklem (nmp->nm_state & NFSSTA_LOCKTIMEO)) 1438191783Srmacklem vq.vq_flags |= VQ_NOTRESPLOCK; 1439191783Srmacklem#endif 1440191783Srmacklem error = SYSCTL_OUT(req, &vq, sizeof(vq)); 1441191783Srmacklem break; 1442191783Srmacklem case VFS_CTL_TIMEO: 1443191783Srmacklem if (req->oldptr != NULL) { 1444191783Srmacklem error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay, 1445191783Srmacklem sizeof(nmp->nm_tprintf_initial_delay)); 1446191783Srmacklem if (error) 1447191783Srmacklem return (error); 1448191783Srmacklem } 1449191783Srmacklem if (req->newptr != NULL) { 1450191783Srmacklem error = vfs_suser(mp, req->td); 1451191783Srmacklem if (error) 1452191783Srmacklem return (error); 1453191783Srmacklem error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay, 1454191783Srmacklem sizeof(nmp->nm_tprintf_initial_delay)); 1455191783Srmacklem if (error) 1456191783Srmacklem return (error); 1457191783Srmacklem if (nmp->nm_tprintf_initial_delay < 0) 1458191783Srmacklem nmp->nm_tprintf_initial_delay = 0; 1459191783Srmacklem } 1460191783Srmacklem break; 1461191783Srmacklem default: 1462191783Srmacklem return (ENOTSUP); 1463191783Srmacklem } 1464191783Srmacklem return (0); 1465191783Srmacklem} 1466191783Srmacklem 1467214048Srmacklem/* 1468214048Srmacklem * Extract the information needed by the nlm from the nfs vnode. 1469214048Srmacklem */ 1470214048Srmacklemstatic void 1471214053Srmacklemnfs_getnlminfo(struct vnode *vp, uint8_t *fhp, size_t *fhlenp, 1472216931Srmacklem struct sockaddr_storage *sp, int *is_v3p, off_t *sizep, 1473216931Srmacklem struct timeval *timeop) 1474214048Srmacklem{ 1475214048Srmacklem struct nfsmount *nmp; 1476214048Srmacklem struct nfsnode *np = VTONFS(vp); 1477214048Srmacklem 1478214048Srmacklem nmp = VFSTONFS(vp->v_mount); 1479214048Srmacklem if (fhlenp != NULL) 1480214053Srmacklem *fhlenp = (size_t)np->n_fhp->nfh_len; 1481214048Srmacklem if (fhp != NULL) 1482214048Srmacklem bcopy(np->n_fhp->nfh_fh, fhp, np->n_fhp->nfh_len); 1483214048Srmacklem if (sp != NULL) 1484214048Srmacklem bcopy(nmp->nm_nam, sp, min(nmp->nm_nam->sa_len, sizeof(*sp))); 1485214048Srmacklem if (is_v3p != NULL) 1486214048Srmacklem *is_v3p = NFS_ISV3(vp); 1487214048Srmacklem if (sizep != NULL) 1488214048Srmacklem *sizep = np->n_size; 1489216931Srmacklem if (timeop != NULL) { 1490216931Srmacklem timeop->tv_sec = nmp->nm_timeo / NFS_HZ; 1491216931Srmacklem timeop->tv_usec = (nmp->nm_timeo % NFS_HZ) * (1000000 / NFS_HZ); 1492216931Srmacklem } 1493214048Srmacklem} 1494214048Srmacklem 1495