/* * Copyright (c) 1989 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Rick Macklem at The University of Guelph. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)nfs_vfsops.c 7.31 (Berkeley) 5/6/91 */ #include "param.h" #include "conf.h" #include "ioctl.h" #include "signal.h" #include "proc.h" #include "namei.h" #include "vnode.h" #include "mount.h" #include "buf.h" #include "mbuf.h" #include "socket.h" #include "systm.h" #include "../net/if.h" #include "../net/route.h" #include "../netinet/in.h" #include "nfsv2.h" #include "nfsnode.h" #include "nfsmount.h" #include "nfs.h" #include "xdr_subs.h" #include "nfsm_subs.h" #include "nfsdiskless.h" /* * nfs vfs operations. */ struct vfsops nfs_vfsops = { nfs_mount, nfs_start, nfs_unmount, nfs_root, nfs_quotactl, nfs_statfs, nfs_sync, nfs_fhtovp, nfs_vptofh, nfs_init, }; static u_char nfs_mntid; extern u_long nfs_procids[NFS_NPROCS]; extern u_long nfs_prog, nfs_vers; struct nfs_diskless nfs_diskless; void nfs_disconnect(); #define TRUE 1 #define FALSE 0 /* * nfs statfs call */ nfs_statfs(mp, sbp, p) struct mount *mp; register struct statfs *sbp; struct proc *p; { register struct vnode *vp; register struct nfsv2_statfs *sfp; register caddr_t cp; register long t1; caddr_t bpos, dpos, cp2; u_long xid; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct nfsmount *nmp; struct ucred *cred; struct nfsnode *np; nmp = VFSTONFS(mp); if (error = nfs_nget(mp, &nmp->nm_fh, &np)) return (error); vp = NFSTOV(np); nfsstats.rpccnt[NFSPROC_STATFS]++; cred = crget(); cred->cr_ngroups = 1; nfsm_reqhead(nfs_procids[NFSPROC_STATFS], cred, NFSX_FH); nfsm_fhtom(vp); nfsm_request(vp, NFSPROC_STATFS, p, 0); nfsm_disect(sfp, struct nfsv2_statfs *, NFSX_STATFS); sbp->f_type = MOUNT_NFS; sbp->f_flags = nmp->nm_flag; sbp->f_bsize = fxdr_unsigned(long, sfp->sf_tsize); sbp->f_fsize = fxdr_unsigned(long, sfp->sf_bsize); sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks); sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree); sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail); sbp->f_files = 0; sbp->f_ffree = 0; if (sbp != &mp->mnt_stat) { bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } nfsm_reqdone; nfs_nput(vp); crfree(cred); return (error); } /* * Mount a remote root fs via. nfs. This depends on the info in the * nfs_diskless structure that has been filled in properly by some primary * bootstrap. * It goes something like this: * - do enough of "ifconfig" by calling ifioctl() so that the system * can talk to the server * - If nfs_diskless.mygateway is filled in, use that address as * a default gateway. * (This is done the 4.3 way with rtioctl() and should be changed) * - hand craft the swap nfs vnode hanging off a fake mount point * - build the rootfs mount point and call mountnfs() to do the rest. */ nfs_mountroot() { register struct mount *mp; register struct mbuf *m; struct socket *so; struct vnode *vp; int error; /* * Do enough of ifconfig(8) so that critical net interface can * talk to the server. */ if (socreate(nfs_diskless.myif.ifra_addr.sa_family, &so, SOCK_DGRAM, 0)) panic("nfs ifconf"); if (ifioctl(so, SIOCAIFADDR, &nfs_diskless.myif)) panic("nfs ifconf2"); soclose(so); /* * If the gateway field is filled in, set it as the default route. */ #ifdef COMPAT_43 if (nfs_diskless.mygateway.sa_family == AF_INET) { struct ortentry rt; struct sockaddr_in *sin; sin = (struct sockaddr_in *) &rt.rt_dst; sin->sin_len = sizeof (struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_addr.s_addr = 0; /* default */ bcopy((caddr_t)&nfs_diskless.mygateway, (caddr_t)&rt.rt_gateway, sizeof (struct sockaddr_in)); rt.rt_flags = (RTF_UP | RTF_GATEWAY); if (rtioctl(SIOCADDRT, (caddr_t)&rt)) panic("nfs root route"); } #endif /* COMPAT_43 */ /* * If swapping to an nfs node (indicated by swdevt[0].sw_dev == NODEV): * Create a fake mount point just for the swap vnode so that the * swap file can be on a different server from the rootfs. */ if (swdevt[0].sw_dev == NODEV) { mp = (struct mount *)malloc((u_long)sizeof(struct mount), M_MOUNT, M_NOWAIT); if (mp == NULL) panic("nfs root mount"); mp->mnt_op = &nfs_vfsops; mp->mnt_flag = 0; mp->mnt_exroot = 0; mp->mnt_mounth = NULLVP; /* * Set up the diskless nfs_args for the swap mount point * and then call mountnfs() to mount it. * Since the swap file is not the root dir of a file system, * hack it to a regular file. */ nfs_diskless.swap_args.fh = (nfsv2fh_t *)nfs_diskless.swap_fh; MGET(m, MT_SONAME, M_DONTWAIT); if (m == NULL) panic("nfs root mbuf"); bcopy((caddr_t)&nfs_diskless.swap_saddr, mtod(m, caddr_t), nfs_diskless.swap_saddr.sa_len); m->m_len = nfs_diskless.swap_saddr.sa_len; if (mountnfs(&nfs_diskless.swap_args, mp, m, "/swap", nfs_diskless.swap_hostnam, &vp)) panic("nfs swap"); vp->v_type = VREG; vp->v_flag = 0; swapdev_vp = vp; VREF(vp); swdevt[0].sw_vp = vp; } /* * Create the rootfs mount point. */ mp = (struct mount *)malloc((u_long)sizeof(struct mount), M_MOUNT, M_NOWAIT); if (mp == NULL) panic("nfs root mount2"); mp->mnt_op = &nfs_vfsops; mp->mnt_flag = MNT_RDONLY; mp->mnt_exroot = 0; mp->mnt_mounth = NULLVP; /* * Set up the root fs args and call mountnfs() to do the rest. */ nfs_diskless.root_args.fh = (nfsv2fh_t *)nfs_diskless.root_fh; MGET(m, MT_SONAME, M_DONTWAIT); if (m == NULL) panic("nfs root mbuf2"); bcopy((caddr_t)&nfs_diskless.root_saddr, mtod(m, caddr_t), nfs_diskless.root_saddr.sa_len); m->m_len = nfs_diskless.root_saddr.sa_len; if (mountnfs(&nfs_diskless.root_args, mp, m, "/", nfs_diskless.root_hostnam, &vp)) panic("nfs root"); if (vfs_lock(mp)) panic("nfs root2"); rootfs = mp; mp->mnt_next = mp; mp->mnt_prev = mp; mp->mnt_vnodecovered = NULLVP; vfs_unlock(mp); rootvp = vp; inittodr((time_t)0); /* There is no time in the nfs fsstat so ?? */ return (0); } /* * VFS Operations. * * mount system call * It seems a bit dumb to copyinstr() the host and path here and then * bcopy() them in mountnfs(), but I wanted to detect errors before * doing the sockargs() call because sockargs() allocates an mbuf and * an error after that means that I have to release the mbuf. */ /* ARGSUSED */ nfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { int error; struct nfs_args args; struct mbuf *nam; struct vnode *vp; char pth[MNAMELEN], hst[MNAMELEN]; u_int len; nfsv2fh_t nfh; if (mp->mnt_flag & MNT_UPDATE) return (0); if (error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args))) return (error); if (error = copyin((caddr_t)args.fh, (caddr_t)&nfh, sizeof (nfsv2fh_t))) return (error); if (error = copyinstr(path, pth, MNAMELEN-1, &len)) return (error); bzero(&pth[len], MNAMELEN - len); if (error = copyinstr(args.hostname, hst, MNAMELEN-1, &len)) return (error); bzero(&hst[len], MNAMELEN - len); /* sockargs() call must be after above copyin() calls */ if (error = sockargs(&nam, (caddr_t)args.addr, sizeof (struct sockaddr), MT_SONAME)) return (error); args.fh = &nfh; error = mountnfs(&args, mp, nam, pth, hst, &vp); return (error); } /* * Common code for mount and mountroot */ mountnfs(argp, mp, nam, pth, hst, vpp) register struct nfs_args *argp; register struct mount *mp; struct mbuf *nam; char *pth, *hst; struct vnode **vpp; { register struct nfsmount *nmp; struct proc *p = curproc; /* XXX */ struct nfsnode *np; int error; fsid_t tfsid; MALLOC(nmp, struct nfsmount *, sizeof *nmp, M_NFSMNT, M_WAITOK); bzero((caddr_t)nmp, sizeof *nmp); mp->mnt_data = (qaddr_t)nmp; /* * Generate a unique nfs mount id. The problem is that a dev number * is not unique across multiple systems. The techique is as follows: * 1) Set to nblkdev,0 which will never be used otherwise * 2) Generate a first guess as nblkdev,nfs_mntid where nfs_mntid is * NOT 0 * 3) Loop searching the mount list for another one with same id * If a match, increment val[0] and try again * NB: I increment val[0] { a long } instead of nfs_mntid { a u_char } * so that nfs is not limited to 255 mount points * Incrementing the high order bits does no real harm, since it * simply makes the major dev number tick up. The upper bound is * set to major dev 127 to avoid any sign extention problems */ mp->mnt_stat.f_fsid.val[0] = makedev(nblkdev, 0); mp->mnt_stat.f_fsid.val[1] = MOUNT_NFS; if (++nfs_mntid == 0) ++nfs_mntid; tfsid.val[0] = makedev(nblkdev, nfs_mntid); tfsid.val[1] = MOUNT_NFS; while (rootfs && getvfs(&tfsid)) { tfsid.val[0]++; nfs_mntid++; } if (major(tfsid.val[0]) > 127) { error = ENOENT; goto bad; } mp->mnt_stat.f_fsid.val[0] = tfsid.val[0]; nmp->nm_mountp = mp; nmp->nm_flag = argp->flags; nmp->nm_rto = NFS_TIMEO; nmp->nm_rtt = -1; nmp->nm_rttvar = nmp->nm_rto << 1; nmp->nm_retry = NFS_RETRANS; nmp->nm_wsize = NFS_WSIZE; nmp->nm_rsize = NFS_RSIZE; bcopy((caddr_t)argp->fh, (caddr_t)&nmp->nm_fh, sizeof(nfsv2fh_t)); bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN); bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN); nmp->nm_nam = nam; if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) { nmp->nm_rto = argp->timeo; /* NFS timeouts are specified in 1/10 sec. */ nmp->nm_rto = (nmp->nm_rto * 10) / NFS_HZ; if (nmp->nm_rto < NFS_MINTIMEO) nmp->nm_rto = NFS_MINTIMEO; else if (nmp->nm_rto > NFS_MAXTIMEO) nmp->nm_rto = NFS_MAXTIMEO; nmp->nm_rttvar = nmp->nm_rto << 1; } if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) { nmp->nm_retry = argp->retrans; if (nmp->nm_retry > NFS_MAXREXMIT) nmp->nm_retry = NFS_MAXREXMIT; } if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) { nmp->nm_wsize = argp->wsize; /* Round down to multiple of blocksize */ nmp->nm_wsize &= ~0x1ff; if (nmp->nm_wsize <= 0) nmp->nm_wsize = 512; else if (nmp->nm_wsize > NFS_MAXDATA) nmp->nm_wsize = NFS_MAXDATA; } if (nmp->nm_wsize > MAXBSIZE) nmp->nm_wsize = MAXBSIZE; if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) { nmp->nm_rsize = argp->rsize; /* Round down to multiple of blocksize */ nmp->nm_rsize &= ~0x1ff; if (nmp->nm_rsize <= 0) nmp->nm_rsize = 512; else if (nmp->nm_rsize > NFS_MAXDATA) nmp->nm_rsize = NFS_MAXDATA; } if (nmp->nm_rsize > MAXBSIZE) nmp->nm_rsize = MAXBSIZE; /* Set up the sockets and per-host congestion */ nmp->nm_sotype = argp->sotype; nmp->nm_soproto = argp->proto; if (error = nfs_connect(nmp)) goto bad; if (error = nfs_statfs(mp, &mp->mnt_stat, p)) goto bad; /* * A reference count is needed on the nfsnode representing the * remote root. If this object is not persistent, then backward * traversals of the mount point (i.e. "..") will not work if * the nfsnode gets flushed out of the cache. Ufs does not have * this problem, because one can identify root inodes by their * number == ROOTINO (2). */ if (error = nfs_nget(mp, &nmp->nm_fh, &np)) goto bad; /* * Unlock it, but keep the reference count. */ nfs_unlock(NFSTOV(np)); *vpp = NFSTOV(np); return (0); bad: nfs_disconnect(nmp); FREE(nmp, M_NFSMNT); m_freem(nam); return (error); } /* * unmount system call */ nfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { register struct nfsmount *nmp; struct nfsnode *np; struct vnode *vp; int error, flags = 0; extern int doforce; if (mntflags & MNT_FORCE) { if (!doforce || mp == rootfs) return (EINVAL); flags |= FORCECLOSE; } nmp = VFSTONFS(mp); /* * Clear out the buffer cache */ mntflushbuf(mp, 0); if (mntinvalbuf(mp)) return (EBUSY); /* * Goes something like this.. * - Check for activity on the root vnode (other than ourselves). * - Call vflush() to clear out vnodes for this file system, * except for the root vnode. * - Decrement reference on the vnode representing remote root. * - Close the socket * - Free up the data structures */ /* * We need to decrement the ref. count on the nfsnode representing * the remote root. See comment in mountnfs(). The VFS unmount() * has done vput on this vnode, otherwise we would get deadlock! */ if (error = nfs_nget(mp, &nmp->nm_fh, &np)) return(error); vp = NFSTOV(np); if (vp->v_usecount > 2) { vput(vp); return (EBUSY); } if (error = vflush(mp, vp, flags)) { vput(vp); return (error); } /* * Get rid of two reference counts, and unlock it on the second. */ vrele(vp); vput(vp); nfs_disconnect(nmp); m_freem(nmp->nm_nam); free((caddr_t)nmp, M_NFSMNT); return (0); } /* * Return root of a filesystem */ nfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { register struct vnode *vp; struct nfsmount *nmp; struct nfsnode *np; int error; nmp = VFSTONFS(mp); if (error = nfs_nget(mp, &nmp->nm_fh, &np)) return (error); vp = NFSTOV(np); vp->v_type = VDIR; vp->v_flag = VROOT; *vpp = vp; return (0); } extern int syncprt; /* * Flush out the buffer cache */ /* ARGSUSED */ nfs_sync(mp, waitfor) struct mount *mp; int waitfor; { if (syncprt) bufstats(); /* * Force stale buffer cache information to be flushed. */ mntflushbuf(mp, waitfor == MNT_WAIT ? B_SYNC : 0); return (0); } /* * At this point, this should never happen */ /* ARGSUSED */ nfs_fhtovp(mp, fhp, vpp) struct mount *mp; struct fid *fhp; struct vnode **vpp; { return (EINVAL); } /* * Vnode pointer to File handle, should never happen either */ /* ARGSUSED */ nfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return (EINVAL); } /* * Vfs start routine, a no-op. */ /* ARGSUSED */ nfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); } /* * Do operations associated with quotas, not supported */ nfs_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { #ifdef lint mp = mp; cmd = cmd; uid = uid; arg = arg; #endif /* lint */ return (EOPNOTSUPP); }