summaryrefslogtreecommitdiffstats
path: root/sys/nfs4client/nfs4_vfsops.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/nfs4client/nfs4_vfsops.c')
-rw-r--r--sys/nfs4client/nfs4_vfsops.c938
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 0000000..fe168f8
--- /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);
+}
OpenPOWER on IntegriCloud