diff options
author | Alfred Perlstein <alfred@FreeBSD.org> | 2003-11-14 20:54:10 +0000 |
---|---|---|
committer | Alfred Perlstein <alfred@FreeBSD.org> | 2003-11-14 20:54:10 +0000 |
commit | 1bf8720450ed336695ad67508a8a88333b0598ef (patch) | |
tree | 551663ad558af360faf7b32b0b3e67a667de012c /sys/nfs4client/nfs4_vfsops.c | |
parent | db049820e129396e767cb948b746b0b0ae1d7440 (diff) | |
download | src-1bf8720450ed336695ad67508a8a88333b0598ef.tar.gz src-1bf8720450ed336695ad67508a8a88333b0598ef.zip |
University of Michigan's Citi NFSv4 kernel client code.
Submitted by: Jim Rees <rees@umich.edu>
Notes
Notes:
svn path=/head/; revision=122698
Diffstat (limited to 'sys/nfs4client/nfs4_vfsops.c')
-rw-r--r-- | sys/nfs4client/nfs4_vfsops.c | 938 |
1 files changed, 938 insertions, 0 deletions
diff --git a/sys/nfs4client/nfs4_vfsops.c b/sys/nfs4client/nfs4_vfsops.c new file mode 100644 index 000000000000..fe168f8aa993 --- /dev/null +++ b/sys/nfs4client/nfs4_vfsops.c @@ -0,0 +1,938 @@ +/* $FreeBSD$ */ +/* $Id: nfs_vfsops.c,v 1.38 2003/11/05 14:59:01 rees Exp $ */ + +/* + * copyright (c) 2003 + * the regents of the university of michigan + * all rights reserved + * + * permission is granted to use, copy, create derivative works and redistribute + * this software and such derivative works for any purpose, so long as the name + * of the university of michigan is not used in any advertising or publicity + * pertaining to the use or distribution of this software without specific, + * written prior authorization. if the above copyright notice or any other + * identification of the university of michigan is included in any copy of any + * portion of this software, then the disclaimer below must also be included. + * + * this software is provided as is, without representation from the university + * of michigan as to its fitness for any purpose, and without warranty by the + * university of michigan of any kind, either express or implied, including + * without limitation the implied warranties of merchantability and fitness for + * a particular purpose. the regents of the university of michigan shall not be + * liable for any damages, including special, indirect, incidental, or + * consequential damages, with respect to any claim arising out of or in + * connection with the use of the software, even if it has been or is hereafter + * advised of the possibility of such damages. + */ + +/* + * Copyright (c) 1989, 1993, 1995 + * 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 8.12 (Berkeley) 5/20/95 + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_bootp.h" +#include "opt_nfsroot.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/kthread.h> +#include <sys/limits.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/module.h> +#include <sys/mount.h> +#include <sys/proc.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <sys/unistd.h> +#include <sys/vnode.h> + +#include <vm/vm.h> +#include <vm/vm_extern.h> +#include <vm/uma.h> + +#include <net/if.h> +#include <net/route.h> +#include <netinet/in.h> +#include <netinet/in_var.h> + +#include <rpc/rpcclnt.h> + +#include <nfs/rpcv2.h> +#include <nfs/nfsproto.h> +#include <nfsclient/nfs.h> +#include <nfs4client/nfs4.h> +#include <nfsclient/nfsnode.h> +#include <nfsclient/nfsmount.h> +#include <nfs/xdr_subs.h> +#include <nfsclient/nfsm_subs.h> +#include <nfsclient/nfsdiskless.h> + +#include <nfs4client/nfs4m_subs.h> +#include <nfs4client/nfs4_vfs.h> + +#include <nfs4client/nfs4_dev.h> +#include <nfs4client/nfs4_idmap.h> + +SYSCTL_NODE(_vfs, OID_AUTO, nfs4, CTLFLAG_RW, 0, "NFS4 filesystem"); +SYSCTL_STRUCT(_vfs_nfs4, NFS_NFSSTATS, nfsstats, CTLFLAG_RD, + &nfsstats, nfsstats, "S,nfsstats"); +#ifdef NFS_DEBUG +int nfs_debug; +SYSCTL_INT(_vfs_nfs4, OID_AUTO, debug, CTLFLAG_RW, &nfs_debug, 0, ""); +#endif + +static void nfs_decode_args(struct nfsmount *nmp, struct nfs_args *argp); +static void nfs4_daemon(void *arg); +static int mountnfs(struct nfs_args *, struct mount *, + struct sockaddr *, char *, char *, struct vnode **, + struct ucred *cred); +static int nfs4_do_setclientid(struct nfsmount *nmp, struct ucred *cred); +static vfs_mount_t nfs_mount; +static vfs_unmount_t nfs_unmount; +static vfs_root_t nfs_root; +static vfs_statfs_t nfs_statfs; +static vfs_sync_t nfs_sync; + +/* + * nfs vfs operations. + */ +static struct vfsops nfs_vfsops = { + .vfs_init = nfs4_init, + .vfs_mount = nfs_mount, + .vfs_root = nfs_root, + .vfs_statfs = nfs_statfs, + .vfs_sync = nfs_sync, + .vfs_uninit = nfs4_uninit, + .vfs_unmount = nfs_unmount, +}; +VFS_SET(nfs_vfsops, nfs4, VFCF_NETWORK); + +/* So that loader and kldload(2) can find us, wherever we are.. */ +MODULE_VERSION(nfs4, 1); + +void nfsargs_ntoh(struct nfs_args *); + +#ifdef NFS4DISKLESS +/* + * This structure must be filled in by a primary bootstrap or bootstrap + * server for a diskless/dataless machine. It is initialized below just + * to ensure that it is allocated to initialized data (.data not .bss). + */ +struct nfs_diskless nfs_diskless = { { { 0 } } }; +struct nfsv3_diskless nfsv3_diskless = { { { 0 } } }; +int nfs_diskless_valid = 0; + +SYSCTL_INT(_vfs_nfs4, OID_AUTO, diskless_valid, CTLFLAG_RD, + &nfs_diskless_valid, 0, ""); + +SYSCTL_STRING(_vfs_nfs4, OID_AUTO, diskless_rootpath, CTLFLAG_RD, + nfsv3_diskless.root_hostnam, 0, ""); + +SYSCTL_OPAQUE(_vfs_nfs4, OID_AUTO, diskless_rootaddr, CTLFLAG_RD, + &nfsv3_diskless.root_saddr, sizeof nfsv3_diskless.root_saddr, + "%Ssockaddr_in", ""); + +SYSCTL_STRING(_vfs_nfs4, OID_AUTO, diskless_swappath, CTLFLAG_RD, + nfsv3_diskless.swap_hostnam, 0, ""); + +SYSCTL_OPAQUE(_vfs_nfs4, OID_AUTO, diskless_swapaddr, CTLFLAG_RD, + &nfsv3_diskless.swap_saddr, sizeof nfsv3_diskless.swap_saddr, + "%Ssockaddr_in",""); + +static int nfs_mountdiskless(char *, char *, int, + struct sockaddr_in *, struct nfs_args *, + struct thread *, struct vnode **, struct mount **); +static void nfs_convert_diskless(void); +static void nfs_convert_oargs(struct nfs_args *args, + struct onfs_args *oargs); + +static void +nfs_convert_oargs(struct nfs_args *args, struct onfs_args *oargs) +{ + + args->version = NFS_ARGSVERSION; + args->addr = oargs->addr; + args->addrlen = oargs->addrlen; + args->sotype = oargs->sotype; + args->proto = oargs->proto; + args->fh = oargs->fh; + args->fhsize = oargs->fhsize; + args->flags = oargs->flags; + args->wsize = oargs->wsize; + args->rsize = oargs->rsize; + args->readdirsize = oargs->readdirsize; + args->timeo = oargs->timeo; + args->retrans = oargs->retrans; + args->maxgrouplist = oargs->maxgrouplist; + args->readahead = oargs->readahead; + args->deadthresh = oargs->deadthresh; + args->hostname = oargs->hostname; +} + +static void +nfs_convert_diskless(void) +{ + + bcopy(&nfs_diskless.myif, &nfsv3_diskless.myif, + sizeof(struct ifaliasreq)); + bcopy(&nfs_diskless.mygateway, &nfsv3_diskless.mygateway, + sizeof(struct sockaddr_in)); + nfs_convert_oargs(&nfsv3_diskless.root_args,&nfs_diskless.root_args); + nfsv3_diskless.root_fhsize = NFSX_V2FH; + bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_V2FH); + bcopy(&nfs_diskless.root_saddr,&nfsv3_diskless.root_saddr, + sizeof(struct sockaddr_in)); + bcopy(nfs_diskless.root_hostnam, nfsv3_diskless.root_hostnam, MNAMELEN); + nfsv3_diskless.root_time = nfs_diskless.root_time; + bcopy(nfs_diskless.my_hostnam, nfsv3_diskless.my_hostnam, + MAXHOSTNAMELEN); + nfs_diskless_valid = 3; +} +#endif + +int +nfs4_init(struct vfsconf *vfsp) +{ + + rpcclnt_init(); + nfs4dev_init(); + idmap_init(); + nfsm_v4init(); + + return (0); +} + +int +nfs4_uninit(struct vfsconf *vfsp) +{ + + rpcclnt_uninit(); + nfs4dev_uninit(); + idmap_uninit(); + + return (0); +} + +/* + * nfs statfs call + */ +static int +nfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td) +{ + struct vnode *vp; + struct nfs_statfs *sfp; + caddr_t bpos, dpos; + struct nfsmount *nmp = VFSTONFS(mp); + int error = 0; + struct mbuf *mreq, *mrep = NULL, *md, *mb; + struct nfsnode *np; + struct nfs4_compound cp; + struct nfs4_oparg_getattr ga; + struct nfsv4_fattr *fap = &ga.fa; + +#ifndef nolint + sfp = NULL; +#endif + error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np); + if (error) + return (error); + vp = NFSTOV(np); + nfsstats.rpccnt[NFSPROC_FSSTAT]++; + mreq = nfsm_reqhead(vp, NFSV4PROC_COMPOUND, NFSX_FH(1)); + mb = mreq; + bpos = mtod(mb, caddr_t); + + ga.bm = &nfsv4_fsattrbm; + nfs_v4initcompound(&cp); + + nfsm_v4build_compound(&cp, "statfs()"); + nfsm_v4build_putfh(&cp, vp); + nfsm_v4build_getattr(&cp, &ga); + nfsm_v4build_finalize(&cp); + + nfsm_request(vp, NFSV4PROC_COMPOUND, td, td->td_ucred); + if (error != 0) + goto nfsmout; + + nfsm_v4dissect_compound(&cp); + nfsm_v4dissect_putfh(&cp); + nfsm_v4dissect_getattr(&cp, &ga); + + nfs4_vfsop_statfs(fap, sbp, mp); + +nfsmout: + error = nfs_v4postop(&cp, error); + + vput(vp); + if (mrep != NULL) + m_freem(mrep); + + return (error); +} + +static void +nfs_decode_args(struct nfsmount *nmp, struct nfs_args *argp) +{ + int s; + int adjsock; + int maxio; + + s = splnet(); + /* + * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes + * no sense in that context. + */ + if (argp->sotype == SOCK_STREAM) + nmp->nm_flag &= ~NFSMNT_NOCONN; + + nmp->nm_flag &= ~NFSMNT_RDIRPLUS; + + /* Re-bind if rsrvd port requested and wasn't on one */ + adjsock = !(nmp->nm_flag & NFSMNT_RESVPORT) + && (argp->flags & NFSMNT_RESVPORT); + /* Also re-bind if we're switching to/from a connected UDP socket */ + adjsock |= ((nmp->nm_flag & NFSMNT_NOCONN) != + (argp->flags & NFSMNT_NOCONN)); + + /* Update flags atomically. Don't change the lock bits. */ + nmp->nm_flag = argp->flags | nmp->nm_flag; + splx(s); + + if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) { + nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10; + if (nmp->nm_timeo < NFS_MINTIMEO) + nmp->nm_timeo = NFS_MINTIMEO; + else if (nmp->nm_timeo > NFS_MAXTIMEO) + nmp->nm_timeo = NFS_MAXTIMEO; + } + + 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_NFSV3) { + if (argp->sotype == SOCK_DGRAM) + maxio = NFS_MAXDGRAMDATA; + else + maxio = NFS_MAXDATA; + } else + maxio = NFS_V2MAXDATA; + + if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) { + nmp->nm_wsize = argp->wsize; + /* Round down to multiple of blocksize */ + nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1); + if (nmp->nm_wsize <= 0) + nmp->nm_wsize = NFS_FABLKSIZE; + } + if (nmp->nm_wsize > maxio) + nmp->nm_wsize = maxio; + 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 &= ~(NFS_FABLKSIZE - 1); + if (nmp->nm_rsize <= 0) + nmp->nm_rsize = NFS_FABLKSIZE; + } + if (nmp->nm_rsize > maxio) + nmp->nm_rsize = maxio; + if (nmp->nm_rsize > MAXBSIZE) + nmp->nm_rsize = MAXBSIZE; + + if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) { + nmp->nm_readdirsize = argp->readdirsize; + } + if (nmp->nm_readdirsize > maxio) + nmp->nm_readdirsize = maxio; + if (nmp->nm_readdirsize > nmp->nm_rsize) + nmp->nm_readdirsize = nmp->nm_rsize; + + if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0) + nmp->nm_acregmin = argp->acregmin; + else + nmp->nm_acregmin = NFS_MINATTRTIMO; + if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0) + nmp->nm_acregmax = argp->acregmax; + else + nmp->nm_acregmax = NFS_MAXATTRTIMO; + if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0) + nmp->nm_acdirmin = argp->acdirmin; + else + nmp->nm_acdirmin = NFS_MINDIRATTRTIMO; + if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0) + nmp->nm_acdirmax = argp->acdirmax; + else + nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO; + if (nmp->nm_acdirmin > nmp->nm_acdirmax) + nmp->nm_acdirmin = nmp->nm_acdirmax; + if (nmp->nm_acregmin > nmp->nm_acregmax) + nmp->nm_acregmin = nmp->nm_acregmax; + + if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0) { + if (argp->maxgrouplist <= NFS_MAXGRPS) + nmp->nm_numgrps = argp->maxgrouplist; + else + nmp->nm_numgrps = NFS_MAXGRPS; + } + if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) { + if (argp->readahead <= NFS_MAXRAHEAD) + nmp->nm_readahead = argp->readahead; + else + nmp->nm_readahead = NFS_MAXRAHEAD; + } + if ((argp->flags & NFSMNT_DEADTHRESH) && argp->deadthresh >= 0) { + if (argp->deadthresh <= NFS_MAXDEADTHRESH) + nmp->nm_deadthresh = argp->deadthresh; + else + nmp->nm_deadthresh = NFS_MAXDEADTHRESH; + } + + adjsock |= ((nmp->nm_sotype != argp->sotype) || + (nmp->nm_soproto != argp->proto)); + nmp->nm_sotype = argp->sotype; + nmp->nm_soproto = argp->proto; + + if (nmp->nm_rpcclnt.rc_so && adjsock) { + nfs_safedisconnect(nmp); + if (nmp->nm_sotype == SOCK_DGRAM) { + while (nfs4_connect(nmp)) { + printf("nfs_args: retrying connect\n"); + (void) tsleep((caddr_t)&lbolt, + PSOCK, "nfscon", 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 */ +static int +nfs_mount(struct mount *mp, char *path, caddr_t data, struct nameidata *ndp, + struct thread *td) +{ + int error; + struct nfs_args args; + struct sockaddr *nam; + struct vnode *vp; + char hst[MNAMELEN]; + size_t len; + + if (path == NULL) { + printf("NFSv4: nfs_mountroot not supported\n"); + return EINVAL; + } + error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args)); + if (error) + return (error); + if (args.version != NFS_ARGSVERSION) + return (EPROGMISMATCH); + if (mp->mnt_flag & MNT_UPDATE) { + struct nfsmount *nmp = VFSTONFS(mp); + + if (nmp == NULL) + return (EIO); + /* + * When doing an update, we can't change from or to + * v3, switch lockd strategies or change cookie translation + */ + args.flags = (args.flags & + ~(NFSMNT_NFSV3 | NFSMNT_NFSV4 | NFSMNT_NOLOCKD)) | + (nmp->nm_flag & + (NFSMNT_NFSV3 | NFSMNT_NFSV4 | NFSMNT_NOLOCKD)); + nfs_decode_args(nmp, &args); + return (0); + } + + if (error) + return (error); + error = copyinstr(args.hostname, hst, MNAMELEN-1, &len); + if (error) + return (error); + bzero(&hst[len], MNAMELEN - len); + /* sockargs() call must be after above copyin() calls */ + error = getsockaddr(&nam, (caddr_t)args.addr, args.addrlen); + if (error) + return (error); + error = mountnfs(&args, mp, nam, path, hst, &vp, td->td_ucred); + return (error); +} + +/* + * renew should be done async + * should re-scan mount queue each time + */ +struct proc *nfs4_daemonproc; + +static int +nfs4_do_renew(struct nfsmount *nmp, struct ucred *cred) +{ + struct nfs4_compound cp; + struct mbuf *mreq, *mrep = NULL, *md, *mb; + caddr_t bpos, dpos; + int error; + + mreq = nfsm_reqhead(NULL, NFSV4PROC_COMPOUND, sizeof(uint64_t)); + mb = mreq; + bpos = mtod(mb, caddr_t); + + nfs_v4initcompound(&cp); + + nfsm_v4build_compound(&cp, "nfs4_do_renew()"); + nfsm_v4build_renew(&cp, nmp->nm_clientid); + nfsm_v4build_finalize(&cp); + + nfsm_request_mnt(nmp, NFSV4PROC_COMPOUND, curthread, cred); + if (error != 0) + goto nfsmout; + + nfsm_v4dissect_compound(&cp); + nfsm_v4dissect_renew(&cp); + nmp->nm_last_renewal = time_second; + return (0); + + nfsmout: + error = nfs_v4postop(&cp, error); + + /* XXX */ + if (mrep != NULL) + m_freem(mrep); + return (error); +} + +static void +nfs4_daemon(void *arg) +{ + struct mount *mp; + struct nfsmount *nmp; + int nmounts; + + while (1) { + nmounts = 0; + mtx_lock(&mountlist_mtx); + TAILQ_FOREACH(mp, &mountlist, mnt_list) { + if (strcmp(mp->mnt_vfc->vfc_name, "nfs4") != 0) + continue; + nmounts++; + nmp = VFSTONFS(mp); + if (time_second < nmp->nm_last_renewal + nmp->nm_lease_time - 4) + continue; + mtx_unlock(&mountlist_mtx); + mtx_lock(&Giant); + nfs4_do_renew(nmp, (struct ucred *) arg); + mtx_unlock(&Giant); + mtx_lock(&mountlist_mtx); + } + mtx_unlock(&mountlist_mtx); + + /* Must kill the daemon here, or module unload will cause a panic */ + if (nmounts == 0) { + mtx_lock(&Giant); + nfs4_daemonproc = NULL; + printf("nfsv4 renewd exiting\n"); + kthread_exit(0); + } + tsleep(&nfs4_daemonproc, PVFS, "nfs4", 2 * hz); + } +} + +/* + * Common code for mount and mountroot + */ +static int +mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam, + char *pth, char *hst, struct vnode **vpp, struct ucred *cred) +{ + struct nfsmount *nmp; + char *rpth, *cp1, *cp2; + int nlkup = 0, error; + struct nfs4_compound cp; + struct mbuf *mreq, *mrep = NULL, *md, *mb; + caddr_t bpos, dpos; + struct nfs4_oparg_lookup lkup; + struct nfs4_oparg_getfh gfh; + struct nfs4_oparg_getattr ga; + struct thread *td = curthread; /* XXX */ + + if (mp->mnt_flag & MNT_UPDATE) { + nmp = VFSTONFS(mp); + /* update paths, file handles, etc, here XXX */ + FREE(nam, M_SONAME); + return (0); + } else { + nmp = uma_zalloc(nfsmount_zone, M_WAITOK); + bzero((caddr_t)nmp, sizeof (struct nfsmount)); + TAILQ_INIT(&nmp->nm_bufq); + mp->mnt_data = (qaddr_t)nmp; + } + + vfs_getnewfsid(mp); + nmp->nm_mountp = mp; + nmp->nm_maxfilesize = 0xffffffffLL; + nmp->nm_timeo = NFS_TIMEO; + nmp->nm_retry = NFS_RETRANS; + nmp->nm_wsize = NFS_WSIZE; + nmp->nm_rsize = NFS_RSIZE; + nmp->nm_readdirsize = NFS_READDIRSIZE; + nmp->nm_numgrps = NFS_MAXGRPS; + nmp->nm_readahead = NFS_DEFRAHEAD; + nmp->nm_deadthresh = NFS_MAXDEADTHRESH; + bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN); + bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN); + nmp->nm_nam = nam; + /* Set up the sockets and per-host congestion */ + nmp->nm_sotype = argp->sotype; + nmp->nm_soproto = argp->proto; + /* XXX */ + mp->mnt_stat.f_iosize = PAGE_SIZE; + + /* XXX - this is to make some other code work. Sort of ugly. */ + argp->flags |= (NFSMNT_NFSV3 | NFSMNT_NFSV4); + + nfs_decode_args(nmp, argp); + + if ((error = nfs4_connect(nmp))) + goto bad; + + mreq = nfsm_reqhead(NULL, NFSV4PROC_COMPOUND, NFSX_FH(1)); + mb = mreq; + bpos = mtod(mb, caddr_t); + + ga.bm = &nfsv4_fsinfobm; + nfs_v4initcompound(&cp); + + /* Get remote path */ + rpth = hst; + strsep(&rpth, ":"); + + nfsm_v4build_compound(&cp, "mountnfs()"); + nfsm_v4build_putrootfh(&cp); + for (cp1 = rpth; cp1 && *cp1; cp1 = cp2) { + while (*cp1 == '/') + cp1++; + if (!*cp1) + break; + for (cp2 = cp1; *cp2 && *cp2 != '/'; cp2++) + ; + lkup.name = cp1; + lkup.namelen = cp2 - cp1; + nfsm_v4build_lookup(&cp, &lkup); + nlkup++; + } + nfsm_v4build_getfh(&cp, &gfh); + nfsm_v4build_getattr(&cp, &ga); + nfsm_v4build_finalize(&cp); + + nfsm_request_mnt(nmp, NFSV4PROC_COMPOUND, td, cred); + if (error != 0) + goto nfsmout; + + nfsm_v4dissect_compound(&cp); + nfsm_v4dissect_putrootfh(&cp); + while (nlkup--) + nfsm_v4dissect_lookup(&cp); + nfsm_v4dissect_getfh(&cp, &gfh); + nfsm_v4dissect_getattr(&cp, &ga); + + nfs4_vfsop_fsinfo(&ga.fa, nmp); + nmp->nm_state |= NFSSTA_GOTFSINFO; + + /* Copy root fh into nfsmount. */ + nmp->nm_fhsize = gfh.fh_len; + bcopy(&gfh.fh_val, nmp->nm_fh, nmp->nm_fhsize); + nmp->nm_last_renewal = time_second; + + if ((error = nfs4_do_setclientid(nmp, cred)) != 0) + goto nfsmout; + + /* Start renewd if it isn't already running */ + if (nfs4_daemonproc == NULL) + kthread_create(nfs4_daemon, crdup(cred), &nfs4_daemonproc, + (RFPROC|RFMEM), 0, "nfs4rd"); + + return (0); + nfsmout: + error = nfs_v4postop(&cp, error); + + /* XXX */ + if (mrep != NULL) + m_freem(mrep); +bad: + nfs_disconnect(nmp); + uma_zfree(nfsmount_zone, nmp); + FREE(nam, M_SONAME); + + return (error); +} + +/* + * unmount system call + */ +static int +nfs_unmount(struct mount *mp, int mntflags, struct thread *td) +{ + struct nfsmount *nmp; + int error, flags = 0; + + if (mntflags & MNT_FORCE) + flags |= FORCECLOSE; + nmp = VFSTONFS(mp); + /* + * Goes something like this.. + * - Call vflush() to clear out vnodes for this filesystem + * - Close the socket + * - Free up the data structures + */ + /* In the forced case, cancel any outstanding requests. */ + if (flags & FORCECLOSE) { + error = nfs_nmcancelreqs(nmp); + if (error) + return (error); + nfs4dev_purge(); + } + + error = vflush(mp, 0, flags); + if (error) + return (error); + + /* + * We are now committed to the unmount. + */ + nfs_disconnect(nmp); + FREE(nmp->nm_nam, M_SONAME); + + /* XXX there's a race condition here for SMP */ + wakeup(&nfs4_daemonproc); + + uma_zfree(nfsmount_zone, nmp); + return (0); +} + +/* + * Return root of a filesystem + */ +static int +nfs_root(struct mount *mp, struct vnode **vpp) +{ + struct vnode *vp; + struct nfsmount *nmp; + struct nfsnode *np; + int error; + + nmp = VFSTONFS(mp); + error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np); + if (error) + return (error); + vp = NFSTOV(np); + if (vp->v_type == VNON) + vp->v_type = VDIR; + vp->v_vflag |= VV_ROOT; + *vpp = vp; + + return (0); +} + +/* + * Flush out the buffer cache + */ +/* ARGSUSED */ +static int +nfs_sync(struct mount *mp, int waitfor, struct ucred *cred, struct thread *td) +{ + struct vnode *vp, *vnp; + int error, allerror = 0; + + /* + * Force stale buffer cache information to be flushed. + */ + MNT_ILOCK(mp); +loop: + for (vp = TAILQ_FIRST(&mp->mnt_nvnodelist); + vp != NULL; + vp = vnp) { + /* + * If the vnode that we are about to sync is no longer + * associated with this mount point, start over. + */ + if (vp->v_mount != mp) + goto loop; + vnp = TAILQ_NEXT(vp, v_nmntvnodes); + VI_LOCK(vp); + MNT_IUNLOCK(mp); + if (VOP_ISLOCKED(vp, NULL) || TAILQ_EMPTY(&vp->v_dirtyblkhd) || + waitfor == MNT_LAZY) { + VI_UNLOCK(vp); + MNT_ILOCK(mp); + continue; + } + if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) { + goto loop; + } + error = VOP_FSYNC(vp, cred, waitfor, td); + if (error) + allerror = error; + VOP_UNLOCK(vp, 0, td); + vrele(vp); + + MNT_ILOCK(mp); + } + MNT_IUNLOCK(mp); + return (allerror); +} + +static int +nfs4_do_setclientid(struct nfsmount *nmp, struct ucred *cred) +{ + struct nfs4_oparg_setclientid scid; + struct nfs4_compound cp; + struct mbuf *mreq, *mrep = NULL, *md, *mb; + caddr_t bpos, dpos; + struct route ro; + char *ipsrc = NULL, uaddr[24], name[24]; + int try = 0; + static int seq; + int error; + +#ifndef NFS4_USE_RPCCLNT + return (0); +#endif + if (nmp->nm_clientid) { + printf("nfs4_do_setclientid: already have clientid!\n"); + error = 0; + goto nfsmout; + } + + /* Try not to re-use clientids */ + if (seq == 0) + seq = time_second & 0xffffff; + +#ifdef NFS4_USE_RPCCLNT + scid.cb_netid = (nmp->nm_rpcclnt.rc_sotype == SOCK_STREAM) ? "tcp" : "udp"; +#endif + scid.cb_netid = "tcp"; + scid.cb_netidlen = 3; + scid.cb_prog = 0x1234; /* XXX */ + + /* Do a route lookup to find our source address for talking to this server */ + bzero(&ro, sizeof ro); + +#ifdef NFS4_USE_RPCCLNT + ro.ro_dst = *nmp->nm_rpcclnt.rc_name; +#endif + rtalloc(&ro); + if (ro.ro_rt == NULL) { + error = EHOSTUNREACH; + goto nfsmout; + } + ipsrc = inet_ntoa(IA_SIN(ifatoia(ro.ro_rt->rt_ifa))->sin_addr); + sprintf(uaddr, "%s.12.48", ipsrc); + scid.cb_univaddr = uaddr; + scid.cb_univaddrlen = strlen(uaddr); + RTFREE(ro.ro_rt); + + try_again: + sprintf(name, "%s-%d", ipsrc, seq++); + scid.namelen = strlen(name); + scid.name = name; + nfs_v4initcompound(&cp); + + mreq = nfsm_reqhead(NULL, NFSV4PROC_COMPOUND, NFSX_FH(1)); + mb = mreq; + bpos = mtod(mb, caddr_t); + + nfsm_v4build_compound(&cp, "nfs4_do_setclientid()"); + nfsm_v4build_setclientid(&cp, &scid); + nfsm_v4build_finalize(&cp); + + nfsm_request_mnt(nmp, NFSV4PROC_COMPOUND, curthread, cred); + if (error != 0) + goto nfsmout; + + nfsm_v4dissect_compound(&cp); + nfsm_v4dissect_setclientid(&cp, &scid); + nmp->nm_clientid = scid.clientid; + + error = nfs_v4postop(&cp, error); + + /* Confirm */ + m_freem(mrep); + mreq = nfsm_reqhead(NULL, NFSV4PROC_COMPOUND, NFSX_FH(1)); + mb = mreq; + bpos = mtod(mb, caddr_t); + + nfs_v4initcompound(&cp); + + nfsm_v4build_compound(&cp, "nfs4_do_setclientid() (confirm)"); + nfsm_v4build_setclientid_confirm(&cp, &scid); + nfsm_v4build_finalize(&cp); + + nfsm_request_mnt(nmp, NFSV4PROC_COMPOUND, curthread, cred); + if (error != 0) + goto nfsmout; + + nfsm_v4dissect_compound(&cp); + nfsm_v4dissect_setclientid_confirm(&cp); + + nfsmout: + error = nfs_v4postop(&cp, error); + + if (mrep) + m_freem(mrep); + if (error == NFSERR_CLID_INUSE && (++try < NFS4_SETCLIENTID_MAXTRIES)) + goto try_again; + + return (error); +} |