]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/nfs4client/nfs4_vfsops.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / nfs4client / nfs4_vfsops.c
1 /* $Id: nfs_vfsops.c,v 1.38 2003/11/05 14:59:01 rees Exp $ */
2
3 /*-
4  * copyright (c) 2003
5  * the regents of the university of michigan
6  * all rights reserved
7  * 
8  * permission is granted to use, copy, create derivative works and redistribute
9  * this software and such derivative works for any purpose, so long as the name
10  * of the university of michigan is not used in any advertising or publicity
11  * pertaining to the use or distribution of this software without specific,
12  * written prior authorization.  if the above copyright notice or any other
13  * identification of the university of michigan is included in any copy of any
14  * portion of this software, then the disclaimer below must also be included.
15  * 
16  * this software is provided as is, without representation from the university
17  * of michigan as to its fitness for any purpose, and without warranty by the
18  * university of michigan of any kind, either express or implied, including
19  * without limitation the implied warranties of merchantability and fitness for
20  * a particular purpose. the regents of the university of michigan shall not be
21  * liable for any damages, including special, indirect, incidental, or
22  * consequential damages, with respect to any claim arising out of or in
23  * connection with the use of the software, even if it has been or is hereafter
24  * advised of the possibility of such damages.
25  */
26
27 /*-
28  * Copyright (c) 1989, 1993, 1995
29  *      The Regents of the University of California.  All rights reserved.
30  *
31  * This code is derived from software contributed to Berkeley by
32  * Rick Macklem at The University of Guelph.
33  *
34  * Redistribution and use in source and binary forms, with or without
35  * modification, are permitted provided that the following conditions
36  * are met:
37  * 1. Redistributions of source code must retain the above copyright
38  *    notice, this list of conditions and the following disclaimer.
39  * 2. Redistributions in binary form must reproduce the above copyright
40  *    notice, this list of conditions and the following disclaimer in the
41  *    documentation and/or other materials provided with the distribution.
42  * 4. Neither the name of the University nor the names of its contributors
43  *    may be used to endorse or promote products derived from this software
44  *    without specific prior written permission.
45  *
46  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56  * SUCH DAMAGE.
57  *
58  *      @(#)nfs_vfsops.c        8.12 (Berkeley) 5/20/95
59  */
60
61 #include <sys/cdefs.h>
62 __FBSDID("$FreeBSD$");
63
64 #include "opt_bootp.h"
65 #include "opt_nfsroot.h"
66
67 #include <sys/param.h>
68 #include <sys/systm.h>
69 #include <sys/kernel.h>
70 #include <sys/kthread.h>
71 #include <sys/limits.h>
72 #include <sys/lock.h>
73 #include <sys/malloc.h>
74 #include <sys/mbuf.h>
75 #include <sys/module.h>
76 #include <sys/mount.h>
77 #include <sys/proc.h>
78 #include <sys/socket.h>
79 #include <sys/socketvar.h>
80 #include <sys/sockio.h>
81 #include <sys/sysctl.h>
82 #include <sys/unistd.h>
83 #include <sys/vnode.h>
84 #include <sys/signalvar.h>
85
86 #include <vm/vm.h>
87 #include <vm/vm_extern.h>
88 #include <vm/uma.h>
89
90 #include <net/if.h>
91 #include <net/route.h>
92 #include <netinet/in.h>
93 #include <netinet/in_var.h>
94
95 #include <rpc/rpcclnt.h>
96
97 #include <nfs/rpcv2.h>
98 #include <nfs/nfsproto.h>
99 #include <nfsclient/nfs.h>
100 #include <nfs4client/nfs4.h>
101 #include <nfsclient/nfsnode.h>
102 #include <nfsclient/nfsmount.h>
103 #include <nfs/xdr_subs.h>
104 #include <nfsclient/nfsm_subs.h>
105 #include <nfsclient/nfsdiskless.h>
106
107 #include <nfs4client/nfs4m_subs.h>
108 #include <nfs4client/nfs4_vfs.h>
109
110 #include <nfs4client/nfs4_dev.h>
111 #include <nfs4client/nfs4_idmap.h>
112
113 SYSCTL_NODE(_vfs, OID_AUTO, nfs4, CTLFLAG_RW, 0, "NFS4 filesystem");
114 SYSCTL_STRUCT(_vfs_nfs4, NFS_NFSSTATS, nfsstats, CTLFLAG_RD,
115         &nfsstats, nfsstats, "S,nfsstats");
116
117 static void     nfs4_decode_args(struct nfsmount *nmp, struct nfs_args *argp);
118 static void     nfs4_daemon(void *arg);
119 static int      mountnfs(struct nfs_args *, struct mount *,
120                     struct sockaddr *, char *, struct vnode **,
121                     struct ucred *cred);
122 static int      nfs4_do_setclientid(struct nfsmount *nmp, struct ucred *cred);
123 static vfs_mount_t nfs4_mount;
124 static vfs_cmount_t nfs4_cmount;
125 static vfs_unmount_t nfs4_unmount;
126 static vfs_root_t nfs4_root;
127 static vfs_statfs_t nfs4_statfs;
128 static vfs_sync_t nfs4_sync;
129
130 /*
131  * nfs vfs operations.
132  */
133 static struct vfsops nfs4_vfsops = {
134         .vfs_init =             nfs4_init,
135         .vfs_mount =            nfs4_mount,
136         .vfs_cmount =           nfs4_cmount,
137         .vfs_root =             nfs4_root,
138         .vfs_statfs =           nfs4_statfs,
139         .vfs_sync =             nfs4_sync,
140         .vfs_uninit =           nfs4_uninit,
141         .vfs_unmount =          nfs4_unmount,
142 };
143 VFS_SET(nfs4_vfsops, nfs4, VFCF_NETWORK);
144
145 static struct nfs_rpcops nfs4_rpcops = {
146         nfs4_readrpc,
147         nfs4_writerpc,
148         nfs4_writebp,
149         nfs4_readlinkrpc,
150         nfs4_invaldir,
151         nfs4_commit,
152 };
153
154 /* So that loader and kldload(2) can find us, wherever we are.. */
155 MODULE_VERSION(nfs4, 1);
156
157 void            nfsargs_ntoh(struct nfs_args *);
158
159 int
160 nfs4_init(struct vfsconf *vfsp)
161 {
162
163         rpcclnt_init();
164         nfs4dev_init();
165         idmap_init();
166         nfsm_v4init();
167
168         return (0);
169 }
170
171 int
172 nfs4_uninit(struct vfsconf *vfsp)
173 {
174
175         rpcclnt_uninit();
176         nfs4dev_uninit();
177         idmap_uninit();
178
179         return (0);
180 }
181
182 /*
183  * nfs statfs call
184  */
185 static int
186 nfs4_statfs(struct mount *mp, struct statfs *sbp, struct thread *td)
187 {
188         struct vnode *vp;
189         struct nfs_statfs *sfp;
190         caddr_t bpos, dpos;
191         struct nfsmount *nmp = VFSTONFS(mp);
192         int error = 0;
193         struct mbuf *mreq, *mrep = NULL, *md, *mb;
194         struct nfsnode *np;
195         struct nfs4_compound cp;
196         struct nfs4_oparg_getattr ga;
197         struct nfsv4_fattr *fap = &ga.fa;
198
199 #ifndef nolint
200         sfp = NULL;
201 #endif
202         error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np, LK_EXCLUSIVE);
203         if (error)
204                 return (error);
205         vp = NFSTOV(np);
206         nfsstats.rpccnt[NFSPROC_FSSTAT]++;
207         mreq = nfsm_reqhead(vp, NFSV4PROC_COMPOUND, NFSX_FH(1));
208         mb = mreq;
209         bpos = mtod(mb, caddr_t);
210
211         ga.bm = &nfsv4_fsattrbm;
212         nfs_v4initcompound(&cp);
213
214         nfsm_v4build_compound(&cp, "statfs()");
215         nfsm_v4build_putfh(&cp, vp);
216         nfsm_v4build_getattr(&cp, &ga);
217         nfsm_v4build_finalize(&cp);
218
219         nfsm_request(vp, NFSV4PROC_COMPOUND, td, td->td_ucred);
220         if (error != 0)
221                 goto nfsmout;
222
223         nfsm_v4dissect_compound(&cp);
224         nfsm_v4dissect_putfh(&cp);
225         nfsm_v4dissect_getattr(&cp, &ga);
226
227         nfs4_vfsop_statfs(fap, sbp, mp);
228
229 nfsmout:
230         error = nfs_v4postop(&cp, error);
231
232         vput(vp);
233         if (mrep != NULL)
234                 m_freem(mrep);
235
236         return (error);
237 }
238
239 static void
240 nfs4_decode_args(struct nfsmount *nmp, struct nfs_args *argp)
241 {
242         int s;
243         int adjsock;
244         int maxio;
245
246         s = splnet();
247
248         /*
249          * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
250          * no sense in that context.  Also, set appropriate retransmit
251          * and soft timeout behavior.
252          */
253         if (argp->sotype == SOCK_STREAM) {
254                 nmp->nm_flag &= ~NFSMNT_NOCONN;
255                 nmp->nm_flag |= NFSMNT_DUMBTIMR;
256                 nmp->nm_timeo = NFS_MAXTIMEO;
257                 nmp->nm_retry = NFS_RETRANS_TCP;
258         }
259
260         nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
261
262         /* Re-bind if rsrvd port requested and wasn't on one */
263         adjsock = !(nmp->nm_flag & NFSMNT_RESVPORT)
264                   && (argp->flags & NFSMNT_RESVPORT);
265         /* Also re-bind if we're switching to/from a connected UDP socket */
266         adjsock |= ((nmp->nm_flag & NFSMNT_NOCONN) !=
267                     (argp->flags & NFSMNT_NOCONN));
268
269         /* Update flags atomically.  Don't change the lock bits. */
270         nmp->nm_flag = argp->flags | nmp->nm_flag;
271         splx(s);
272
273         if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
274                 nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
275                 if (nmp->nm_timeo < NFS_MINTIMEO)
276                         nmp->nm_timeo = NFS_MINTIMEO;
277                 else if (nmp->nm_timeo > NFS_MAXTIMEO)
278                         nmp->nm_timeo = NFS_MAXTIMEO;
279         }
280
281         if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
282                 nmp->nm_retry = argp->retrans;
283                 if (nmp->nm_retry > NFS_MAXREXMIT)
284                         nmp->nm_retry = NFS_MAXREXMIT;
285         }
286
287         if (argp->flags & NFSMNT_NFSV3) {
288                 if (argp->sotype == SOCK_DGRAM)
289                         maxio = NFS_MAXDGRAMDATA;
290                 else
291                         maxio = NFS_MAXDATA;
292         } else
293                 maxio = NFS_V2MAXDATA;
294
295         if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
296                 nmp->nm_wsize = argp->wsize;
297                 /* Round down to multiple of blocksize */
298                 nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
299                 if (nmp->nm_wsize <= 0)
300                         nmp->nm_wsize = NFS_FABLKSIZE;
301         }
302         if (nmp->nm_wsize > maxio)
303                 nmp->nm_wsize = maxio;
304         if (nmp->nm_wsize > MAXBSIZE)
305                 nmp->nm_wsize = MAXBSIZE;
306
307         if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
308                 nmp->nm_rsize = argp->rsize;
309                 /* Round down to multiple of blocksize */
310                 nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
311                 if (nmp->nm_rsize <= 0)
312                         nmp->nm_rsize = NFS_FABLKSIZE;
313         }
314         if (nmp->nm_rsize > maxio)
315                 nmp->nm_rsize = maxio;
316         if (nmp->nm_rsize > MAXBSIZE)
317                 nmp->nm_rsize = MAXBSIZE;
318
319         if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
320                 nmp->nm_readdirsize = argp->readdirsize;
321         }
322         if (nmp->nm_readdirsize > maxio)
323                 nmp->nm_readdirsize = maxio;
324         if (nmp->nm_readdirsize > nmp->nm_rsize)
325                 nmp->nm_readdirsize = nmp->nm_rsize;
326
327         if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
328                 nmp->nm_acregmin = argp->acregmin;
329         else
330                 nmp->nm_acregmin = NFS_MINATTRTIMO;
331         if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
332                 nmp->nm_acregmax = argp->acregmax;
333         else
334                 nmp->nm_acregmax = NFS_MAXATTRTIMO;
335         if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
336                 nmp->nm_acdirmin = argp->acdirmin;
337         else
338                 nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
339         if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
340                 nmp->nm_acdirmax = argp->acdirmax;
341         else
342                 nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
343         if (nmp->nm_acdirmin > nmp->nm_acdirmax)
344                 nmp->nm_acdirmin = nmp->nm_acdirmax;
345         if (nmp->nm_acregmin > nmp->nm_acregmax)
346                 nmp->nm_acregmin = nmp->nm_acregmax;
347
348         if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0) {
349                 if (argp->maxgrouplist <= NFS_MAXGRPS)
350                         nmp->nm_numgrps = argp->maxgrouplist;
351                 else
352                         nmp->nm_numgrps = NFS_MAXGRPS;
353         }
354         if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) {
355                 if (argp->readahead <= NFS_MAXRAHEAD)
356                         nmp->nm_readahead = argp->readahead;
357                 else
358                         nmp->nm_readahead = NFS_MAXRAHEAD;
359         }
360         if ((argp->flags & NFSMNT_DEADTHRESH) && argp->deadthresh >= 0) {
361                 if (argp->deadthresh <= NFS_MAXDEADTHRESH)
362                         nmp->nm_deadthresh = argp->deadthresh;
363                 else
364                         nmp->nm_deadthresh = NFS_MAXDEADTHRESH;
365         }
366
367         adjsock |= ((nmp->nm_sotype != argp->sotype) ||
368                     (nmp->nm_soproto != argp->proto));
369         nmp->nm_sotype = argp->sotype;
370         nmp->nm_soproto = argp->proto;
371
372         if (nmp->nm_rpcclnt.rc_so && adjsock) {
373                 nfs_safedisconnect(nmp);
374                 if (nmp->nm_sotype == SOCK_DGRAM) {
375                         while (nfs4_connect(nmp)) {
376                                 printf("nfs4_decode_args: retrying connect\n");
377                                 (void)tsleep(&lbolt, PSOCK, "nfscon", 0);
378                         }
379                 }
380         }
381 }
382
383 /*
384  * VFS Operations.
385  *
386  * mount system call
387  * It seems a bit dumb to copyinstr() the host and path here and then
388  * bcopy() them in mountnfs(), but I wanted to detect errors before
389  * doing the sockargs() call because sockargs() allocates an mbuf and
390  * an error after that means that I have to release the mbuf.
391  */
392 /* ARGSUSED */
393 static int
394 nfs4_cmount(struct mntarg *ma, void *data, int flags, struct thread *td)
395 {
396         struct nfs_args args;
397         int error;
398
399         error = copyin(data, &args, sizeof(struct nfs_args));
400         if (error)
401                 return (error);
402         ma = mount_arg(ma, "nfs_args", &args, sizeof args);
403         error = kernel_mount(ma, flags);
404         return (error);
405 }
406
407 static int
408 nfs4_mount(struct mount *mp, struct thread *td)
409 {
410         int error;
411         struct nfs_args args;
412         struct sockaddr *nam;
413         struct vnode *vp;
414         char hst[MNAMELEN];
415         size_t len;
416
417         if (mp->mnt_flag & MNT_ROOTFS) {
418                 printf("nfs4_mountroot not supported\n");
419                 return (EINVAL);
420         }
421         error = vfs_copyopt(mp->mnt_optnew, "nfs_args", &args, sizeof args);
422         if (error)
423                 return (error);
424
425         if (args.version != NFS_ARGSVERSION)
426                 return (EPROGMISMATCH);
427         if (mp->mnt_flag & MNT_UPDATE) {
428                 struct nfsmount *nmp = VFSTONFS(mp);
429
430                 if (nmp == NULL)
431                         return (EIO);
432                 /*
433                  * When doing an update, we can't change from or to
434                  * v3, switch lockd strategies or change cookie translation
435                  */
436                 args.flags = (args.flags &
437                     ~(NFSMNT_NFSV3 | NFSMNT_NFSV4 | NFSMNT_NOLOCKD)) |
438                     (nmp->nm_flag &
439                         (NFSMNT_NFSV3 | NFSMNT_NFSV4 | NFSMNT_NOLOCKD));
440                 nfs4_decode_args(nmp, &args);
441                 return (0);
442         }
443
444         error = copyinstr(args.hostname, hst, MNAMELEN-1, &len);
445         if (error)
446                 return (error);
447         bzero(&hst[len], MNAMELEN - len);
448         /* sockargs() call must be after above copyin() calls */
449         error = getsockaddr(&nam, (caddr_t)args.addr, args.addrlen);
450         if (error)
451                 return (error);
452         error = mountnfs(&args, mp, nam, hst, &vp, td->td_ucred);
453         return (error);
454 }
455
456 /*
457  * renew should be done async
458  * should re-scan mount queue each time
459  */
460 struct proc *nfs4_daemonproc;
461
462 static int
463 nfs4_do_renew(struct nfsmount *nmp, struct ucred *cred)
464 {
465         struct nfs4_compound cp;
466         struct mbuf *mreq, *mrep = NULL, *md, *mb;
467         caddr_t bpos, dpos;     
468         int error;
469
470         mreq = nfsm_reqhead(NULL, NFSV4PROC_COMPOUND, sizeof(uint64_t));
471         mb = mreq;
472         bpos = mtod(mb, caddr_t);
473
474         nfs_v4initcompound(&cp);
475
476         nfsm_v4build_compound(&cp, "nfs4_do_renew()");
477         nfsm_v4build_renew(&cp, nmp->nm_clientid);
478         nfsm_v4build_finalize(&cp);
479
480         nfsm_request_mnt(nmp, NFSV4PROC_COMPOUND, curthread, cred);
481         if (error != 0)
482                 goto nfsmout;
483
484         nfsm_v4dissect_compound(&cp);
485         nfsm_v4dissect_renew(&cp);
486         nmp->nm_last_renewal = time_second;
487         return (0);
488
489  nfsmout:
490         error = nfs_v4postop(&cp, error);
491
492         /* XXX */
493         if (mrep != NULL)
494                 m_freem(mrep);
495         return (error);
496 }
497
498 static void
499 nfs4_daemon(void *arg)
500 {
501         struct mount *mp;
502         struct nfsmount *nmp;
503         int nmounts;
504
505         while (1) {
506                 nmounts = 0;
507                 mtx_lock(&mountlist_mtx);
508                 TAILQ_FOREACH(mp, &mountlist, mnt_list) {
509                         if (strcmp(mp->mnt_vfc->vfc_name, "nfs4") != 0)
510                                 continue;
511                         nmounts++;
512                         nmp = VFSTONFS(mp);
513                         if (time_second < nmp->nm_last_renewal + nmp->nm_lease_time - 4)
514                                 continue;
515                         mtx_unlock(&mountlist_mtx);
516                         mtx_lock(&Giant);
517                         nfs4_do_renew(nmp, (struct ucred *) arg);
518                         mtx_unlock(&Giant);
519                         mtx_lock(&mountlist_mtx);
520                 }
521                 mtx_unlock(&mountlist_mtx);
522
523                 /* Must kill the daemon here, or module unload will cause a panic */
524                 if (nmounts == 0) {
525                         mtx_lock(&Giant);
526                         nfs4_daemonproc = NULL;
527                         mtx_unlock(&Giant);
528                         /*printf("nfsv4 renewd exiting\n");*/
529                         kthread_exit(0);
530                 }
531                 tsleep(&nfs4_daemonproc, PVFS, "nfs4", 2 * hz);
532         }
533 }
534
535 /*
536  * Common code for mount and mountroot
537  */
538 static int
539 mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
540     char *hst, struct vnode **vpp, struct ucred *cred)
541 {
542         struct nfsmount *nmp;
543         char *rpth, *cp1, *cp2;
544         int nlkup = 0, error;
545         struct nfs4_compound cp;
546         struct mbuf *mreq, *mrep = NULL, *md, *mb;
547         caddr_t bpos, dpos;     
548         struct nfs4_oparg_lookup lkup;
549         struct nfs4_oparg_getfh gfh;
550         struct nfs4_oparg_getattr ga;
551         struct thread *td = curthread; /* XXX */
552
553         if (mp->mnt_flag & MNT_UPDATE) {
554                 nmp = VFSTONFS(mp);
555                 /* update paths, file handles, etc, here        XXX */
556                 FREE(nam, M_SONAME);
557                 return (0);
558         } else {
559                 nmp = uma_zalloc(nfsmount_zone, M_WAITOK);
560                 bzero((caddr_t)nmp, sizeof (struct nfsmount));
561                 TAILQ_INIT(&nmp->nm_bufq);
562                 mp->mnt_data = (qaddr_t)nmp;
563         }
564
565         vfs_getnewfsid(mp);
566         nmp->nm_mountp = mp;
567         mtx_init(&nmp->nm_mtx, "NFS4mount lock", NULL, MTX_DEF);                        
568
569         nmp->nm_maxfilesize = 0xffffffffLL;
570         nmp->nm_timeo = NFS_TIMEO;
571         nmp->nm_retry = NFS_RETRANS;
572         nmp->nm_wsize = NFS_WSIZE;
573         nmp->nm_rsize = NFS_RSIZE;
574         nmp->nm_readdirsize = NFS_READDIRSIZE;
575         nmp->nm_numgrps = NFS_MAXGRPS;
576         nmp->nm_readahead = NFS_DEFRAHEAD;
577         nmp->nm_deadthresh = NFS_MAXDEADTHRESH;
578         vfs_mountedfrom(mp, hst);
579         nmp->nm_nam = nam;
580         /* Set up the sockets and per-host congestion */
581         nmp->nm_sotype = argp->sotype;
582         nmp->nm_soproto = argp->proto;
583         nmp->nm_rpcops = &nfs4_rpcops;
584         /* XXX */
585         mp->mnt_stat.f_iosize = PAGE_SIZE;
586
587         argp->flags |= (NFSMNT_NFSV3 | NFSMNT_NFSV4);
588
589         nfs4_decode_args(nmp, argp);
590
591         if ((error = nfs4_connect(nmp)))
592                 goto bad;
593
594         mreq = nfsm_reqhead(NULL, NFSV4PROC_COMPOUND, NFSX_FH(1));
595         mb = mreq;
596         bpos = mtod(mb, caddr_t);
597
598         ga.bm = &nfsv4_fsinfobm;
599         nfs_v4initcompound(&cp);
600
601         /* Get remote path */
602         rpth = hst;
603         strsep(&rpth, ":");
604
605         nfsm_v4build_compound(&cp, "mountnfs()");
606         nfsm_v4build_putrootfh(&cp);
607         for (cp1 = rpth; cp1 && *cp1; cp1 = cp2)  {
608                 while (*cp1 == '/')
609                         cp1++;
610                 if (!*cp1)
611                         break;
612                 for (cp2 = cp1; *cp2 && *cp2 != '/'; cp2++)
613                         ;
614                 lkup.name = cp1;
615                 lkup.namelen = cp2 - cp1;
616                 nfsm_v4build_lookup(&cp, &lkup);
617                 nlkup++;
618         }
619         nfsm_v4build_getfh(&cp, &gfh);
620         nfsm_v4build_getattr(&cp, &ga);
621         nfsm_v4build_finalize(&cp);
622
623         nfsm_request_mnt(nmp, NFSV4PROC_COMPOUND, td, cred);
624         if (error != 0)
625                 goto nfsmout;
626
627         nfsm_v4dissect_compound(&cp);
628         nfsm_v4dissect_putrootfh(&cp);
629         while (nlkup--)
630                 nfsm_v4dissect_lookup(&cp);
631         nfsm_v4dissect_getfh(&cp, &gfh);
632         nfsm_v4dissect_getattr(&cp, &ga);
633
634         nfs4_vfsop_fsinfo(&ga.fa, nmp);
635         nmp->nm_state |= NFSSTA_GOTFSINFO;
636
637         /* Copy root fh into nfsmount. */
638         nmp->nm_fhsize = gfh.fh_len;
639         bcopy(&gfh.fh_val, nmp->nm_fh, nmp->nm_fhsize);
640         nmp->nm_last_renewal = time_second;
641
642         if ((error = nfs4_do_setclientid(nmp, cred)) != 0)
643                 goto nfsmout;
644
645         /* Start renewd if it isn't already running */
646         if (nfs4_daemonproc == NULL)
647                 kthread_create(nfs4_daemon, crdup(cred), &nfs4_daemonproc,
648                                (RFPROC|RFMEM), 0, "nfs4rd");
649
650         return (0);
651  nfsmout:
652         error = nfs_v4postop(&cp, error);
653
654         /* XXX */
655         if (mrep != NULL)
656                 m_freem(mrep);
657 bad:
658         mtx_destroy(&nmp->nm_mtx);
659         nfs4_disconnect(nmp);
660         uma_zfree(nfsmount_zone, nmp);
661         FREE(nam, M_SONAME);
662
663         return (error);
664 }
665
666 /*
667  * unmount system call
668  */
669 static int
670 nfs4_unmount(struct mount *mp, int mntflags, struct thread *td)
671 {
672         struct nfsmount *nmp;
673         int error, flags = 0;
674
675         if (mntflags & MNT_FORCE)
676                 flags |= FORCECLOSE;
677         nmp = VFSTONFS(mp);
678         /*
679          * Goes something like this..
680          * - Call vflush(, td) to clear out vnodes for this filesystem
681          * - Close the socket
682          * - Free up the data structures
683          */
684         /* In the forced case, cancel any outstanding requests. */
685         if (flags & FORCECLOSE) {
686                 error = nfs_nmcancelreqs(nmp);
687                 if (error)
688                         return (error);
689                 nfs4dev_purge();
690         }
691
692         error = vflush(mp, 0, flags, td);
693         if (error)
694                 return (error);
695
696         /*
697          * We are now committed to the unmount.
698          */
699         nfs4_disconnect(nmp);
700         FREE(nmp->nm_nam, M_SONAME);
701
702         /* XXX there's a race condition here for SMP */
703         wakeup(&nfs4_daemonproc);
704
705         mtx_destroy(&nmp->nm_mtx);
706         uma_zfree(nfsmount_zone, nmp);
707         return (0);
708 }
709
710 /*
711  * Return root of a filesystem
712  */
713 static int
714 nfs4_root(struct mount *mp, int flags, struct vnode **vpp, struct thread *td)
715 {
716         struct vnode *vp;
717         struct nfsmount *nmp;
718         struct nfsnode *np;
719         int error;
720
721         nmp = VFSTONFS(mp);
722         error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np,
723             LK_EXCLUSIVE);
724         if (error)
725                 return (error);
726         vp = NFSTOV(np);
727         if (vp->v_type == VNON)
728             vp->v_type = VDIR;
729         vp->v_vflag |= VV_ROOT;
730         *vpp = vp;
731
732         return (0);
733 }
734
735 /*
736  * Flush out the buffer cache
737  */
738 static int
739 nfs4_sync(struct mount *mp, int waitfor, struct thread *td)
740 {
741         struct vnode *vp, *mvp;
742         int error, allerror = 0;
743
744         /*
745          * Force stale buffer cache information to be flushed.
746          */
747         MNT_ILOCK(mp);
748 loop:
749         MNT_VNODE_FOREACH(vp, mp, mvp) {
750                 VI_LOCK(vp);
751                 MNT_IUNLOCK(mp);
752                 if (VOP_ISLOCKED(vp, NULL) ||
753                     vp->v_bufobj.bo_dirty.bv_cnt == 0 ||
754                     waitfor == MNT_LAZY) {
755                         VI_UNLOCK(vp);
756                         MNT_ILOCK(mp);
757                         continue;
758                 }
759                 if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
760                         MNT_ILOCK(mp);
761                         MNT_VNODE_FOREACH_ABORT_ILOCKED(mp, mvp);
762                         goto loop;
763                 }
764                 error = VOP_FSYNC(vp, waitfor, td);
765                 if (error)
766                         allerror = error;
767                 VOP_UNLOCK(vp, 0, td);
768                 vrele(vp);
769
770                 MNT_ILOCK(mp);
771         }
772         MNT_IUNLOCK(mp);
773         return (allerror);
774 }
775
776 static int
777 nfs4_do_setclientid(struct nfsmount *nmp, struct ucred *cred)
778 {
779         struct nfs4_oparg_setclientid scid;
780         struct nfs4_compound cp;
781         struct mbuf *mreq, *mrep = NULL, *md, *mb;
782         caddr_t bpos, dpos;     
783         struct route ro;
784         char *ipsrc = NULL, uaddr[24], name[24];
785         int try = 0;
786         static unsigned long seq;
787         int error;
788
789 #ifndef NFS4_USE_RPCCLNT
790         return (0);
791 #endif
792         if (nmp->nm_clientid) {
793                 printf("nfs4_do_setclientid: already have clientid!\n");
794                 error = 0;
795                 goto nfsmout;
796         }
797
798         /* Try not to re-use clientids */
799         if (seq == 0)
800                 seq = time_second;
801
802 #ifdef NFS4_USE_RPCCLNT
803         scid.cb_netid = (nmp->nm_rpcclnt.rc_sotype == SOCK_STREAM) ? "tcp" : "udp";
804 #endif
805         scid.cb_netid = "tcp";
806         scid.cb_netidlen = 3;
807         scid.cb_prog = 0x1234; /* XXX */
808
809         /* Do a route lookup to find our source address for talking to this server */
810         bzero(&ro, sizeof ro);
811
812 #ifdef NFS4_USE_RPCCLNT
813         ro.ro_dst = *nmp->nm_rpcclnt.rc_name;
814 #endif
815 /* XXX MRT NFS uses table 0 */
816         in_rtalloc(&ro, 0);
817         if (ro.ro_rt == NULL) {
818                 error = EHOSTUNREACH;
819                 goto nfsmout;
820         }
821         ipsrc = inet_ntoa(IA_SIN(ifatoia(ro.ro_rt->rt_ifa))->sin_addr);
822         sprintf(uaddr, "%s.12.48", ipsrc);
823         scid.cb_univaddr = uaddr;
824         scid.cb_univaddrlen = strlen(uaddr);
825         RTFREE(ro.ro_rt);
826
827  try_again:
828         sprintf(name, "%s-%d", ipsrc, (int) ((seq + try) % 1000000L));
829         scid.namelen = strlen(name);
830         scid.name = name;
831         nfs_v4initcompound(&cp);
832
833         mreq = nfsm_reqhead(NULL, NFSV4PROC_COMPOUND, NFSX_FH(1));
834         mb = mreq;
835         bpos = mtod(mb, caddr_t);
836
837         nfsm_v4build_compound(&cp, "nfs4_do_setclientid()");
838         nfsm_v4build_setclientid(&cp, &scid);
839         nfsm_v4build_finalize(&cp);
840
841         nfsm_request_mnt(nmp, NFSV4PROC_COMPOUND, curthread, cred);
842         if (error != 0)
843                 goto nfsmout;
844
845         nfsm_v4dissect_compound(&cp);
846         nfsm_v4dissect_setclientid(&cp, &scid);
847         nmp->nm_clientid = scid.clientid;
848
849         error = nfs_v4postop(&cp, error);
850
851         /* Confirm */
852         m_freem(mrep);
853         mreq = nfsm_reqhead(NULL, NFSV4PROC_COMPOUND, NFSX_FH(1));
854         mb = mreq;
855         bpos = mtod(mb, caddr_t);
856
857         nfs_v4initcompound(&cp);
858
859         nfsm_v4build_compound(&cp, "nfs4_do_setclientid() (confirm)");
860         nfsm_v4build_setclientid_confirm(&cp, &scid);
861         nfsm_v4build_finalize(&cp);
862
863         nfsm_request_mnt(nmp, NFSV4PROC_COMPOUND, curthread, cred);
864         if (error != 0)
865                 goto nfsmout;
866
867         nfsm_v4dissect_compound(&cp);
868         nfsm_v4dissect_setclientid_confirm(&cp);
869
870  nfsmout:
871         error = nfs_v4postop(&cp, error);
872
873         if (mrep)
874                 m_freem(mrep);
875         if (error == NFSERR_CLID_INUSE && (++try < NFS4_SETCLIENTID_MAXTRIES))
876                 goto try_again;
877
878         return (error);
879 }