]> git.saurik.com Git - apple/xnu.git/blame - bsd/nfs/nfs_vfsops.c
xnu-344.23.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_vfsops.c
CommitLineData
1c79356b 1/*
de355530 2 * Copyright (c) 2000-2001 Apple Computer, Inc. All rights reserved.
1c79356b
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
de355530
A
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
1c79356b 11 *
de355530
A
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1c79356b
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
de355530
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
1c79356b
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
23/*
24 * Copyright (c) 1989, 1993, 1995
25 * The Regents of the University of California. All rights reserved.
26 *
27 * This code is derived from software contributed to Berkeley by
28 * Rick Macklem at The University of Guelph.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 * must display the following acknowledgement:
40 * This product includes software developed by the University of
41 * California, Berkeley and its contributors.
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 * FreeBSD-Id: nfs_vfsops.c,v 1.52 1997/11/12 05:42:21 julian Exp $
1c79356b
A
60 */
61
62#include <sys/param.h>
63#include <sys/systm.h>
64#include <sys/conf.h>
65#include <sys/ioctl.h>
66#include <sys/signal.h>
67#include <sys/proc.h>
68#include <sys/namei.h>
69#include <sys/vnode.h>
70#include <sys/malloc.h>
71#include <sys/kernel.h>
72#include <sys/sysctl.h>
73#include <sys/mount.h>
74#include <sys/buf.h>
75#include <sys/mbuf.h>
76#include <sys/socket.h>
77#include <sys/socketvar.h>
78
79#include <sys/vm.h>
80#include <sys/vmparam.h>
81
82#if !defined(NO_MOUNT_PRIVATE)
83#include <sys/filedesc.h>
84#endif /* NO_MOUNT_PRIVATE */
85
86#include <net/if.h>
87#include <net/route.h>
88#include <netinet/in.h>
89
90#include <nfs/rpcv2.h>
91#include <nfs/nfsproto.h>
92#include <nfs/nfs.h>
93#include <nfs/nfsnode.h>
94#include <nfs/nfsmount.h>
95#include <nfs/xdr_subs.h>
96#include <nfs/nfsm_subs.h>
97#include <nfs/nfsdiskless.h>
98#include <nfs/nqnfs.h>
99
100extern int nfs_mountroot __P((void));
101
102extern int nfs_ticks;
103
104struct nfsstats nfsstats;
105static int nfs_sysctl(int *, u_int, void *, size_t *, void *, size_t,
106 struct proc *);
107/* XXX CSM 11/25/97 Upgrade sysctl.h someday */
108#ifdef notyet
109SYSCTL_NODE(_vfs, MOUNT_NFS, nfs, CTLFLAG_RW, 0, "NFS filesystem");
110SYSCTL_STRUCT(_vfs_nfs, NFS_NFSSTATS, nfsstats, CTLFLAG_RD,
111 &nfsstats, nfsstats, "");
112#endif
113#if NFSDIAG
114int nfs_debug;
115/* XXX CSM 11/25/97 Upgrade sysctl.h someday */
116#ifdef notyet
117SYSCTL_INT(_vfs_nfs, OID_AUTO, debug, CTLFLAG_RW, &nfs_debug, 0, "");
118#endif
119#endif
120
121static int nfs_iosize __P((struct nfsmount *nmp));
122static int mountnfs __P((struct nfs_args *,struct mount *,
123 struct mbuf *,char *,char *,struct vnode **));
124static int nfs_mount __P(( struct mount *mp, char *path, caddr_t data,
125 struct nameidata *ndp, struct proc *p));
126static int nfs_start __P(( struct mount *mp, int flags,
127 struct proc *p));
128static int nfs_unmount __P(( struct mount *mp, int mntflags,
129 struct proc *p));
130static int nfs_root __P(( struct mount *mp, struct vnode **vpp));
131static int nfs_quotactl __P(( struct mount *mp, int cmds, uid_t uid,
132 caddr_t arg, struct proc *p));
133static int nfs_statfs __P(( struct mount *mp, struct statfs *sbp,
134 struct proc *p));
135static int nfs_sync __P(( struct mount *mp, int waitfor,
136 struct ucred *cred, struct proc *p));
137static int nfs_vptofh __P(( struct vnode *vp, struct fid *fhp));
138static int nfs_fhtovp __P((struct mount *mp, struct fid *fhp,
139 struct mbuf *nam, struct vnode **vpp,
140 int *exflagsp, struct ucred **credanonp));
141static int nfs_vget __P((struct mount *, ino_t, struct vnode **));
142
143
144/*
145 * nfs vfs operations.
146 */
147struct vfsops nfs_vfsops = {
148 nfs_mount,
149 nfs_start,
150 nfs_unmount,
151 nfs_root,
152 nfs_quotactl,
153 nfs_statfs,
154 nfs_sync,
155 nfs_vget,
156 nfs_fhtovp,
157 nfs_vptofh,
158 nfs_init,
159 nfs_sysctl
160};
161/* XXX CSM 11/25/97 Mysterious kernel.h ld crud */
162#ifdef notyet
163VFS_SET(nfs_vfsops, nfs, MOUNT_NFS, VFCF_NETWORK);
164#endif
165
de355530
A
166/*
167 * This structure must be filled in by a primary bootstrap or bootstrap
168 * server for a diskless/dataless machine. It is initialized below just
169 * to ensure that it is allocated to initialized data (.data not .bss).
170 */
171struct nfs_diskless nfs_diskless = { 0 };
172int nfs_diskless_valid = 0;
173
174/* XXX CSM 11/25/97 Upgrade sysctl.h someday */
175#ifdef notyet
176SYSCTL_INT(_vfs_nfs, OID_AUTO, diskless_valid, CTLFLAG_RD,
177 &nfs_diskless_valid, 0, "");
178
179SYSCTL_STRING(_vfs_nfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD,
180 nfs_diskless.root_hostnam, 0, "");
181
182SYSCTL_OPAQUE(_vfs_nfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD,
183 &nfs_diskless.root_saddr, sizeof nfs_diskless.root_saddr,
184 "%Ssockaddr_in", "");
185
186SYSCTL_STRING(_vfs_nfs, OID_AUTO, diskless_swappath, CTLFLAG_RD,
187 nfs_diskless.swap_hostnam, 0, "");
188
189SYSCTL_OPAQUE(_vfs_nfs, OID_AUTO, diskless_swapaddr, CTLFLAG_RD,
190 &nfs_diskless.swap_saddr, sizeof nfs_diskless.swap_saddr,
191 "%Ssockaddr_in","");
192#endif
193
1c79356b
A
194
195void nfsargs_ntoh __P((struct nfs_args *));
196static int
197nfs_mount_diskless __P((struct nfs_dlmount *, char *, int, struct vnode **,
198 struct mount **));
199#if !defined(NO_MOUNT_PRIVATE)
200static int
201nfs_mount_diskless_private __P((struct nfs_dlmount *, char *, int,
202 struct vnode **, struct mount **));
203#endif /* NO_MOUNT_PRIVATE */
204static void nfs_convert_oargs __P((struct nfs_args *args,
205 struct onfs_args *oargs));
206#if NFSDIAG
207int nfsreqqusers = 0;
208extern int nfsbtlen, nfsbtcpu, nfsbtthread, nfsbt[32];
209#endif
210
211static int nfs_iosize(nmp)
212 struct nfsmount* nmp;
213{
214 int iosize;
215
216 /*
217 * Calculate the size used for io buffers. Use the larger
218 * of the two sizes to minimise nfs requests but make sure
219 * that it is at least one VM page to avoid wasting buffer
220 * space.
221 */
222 iosize = max(nmp->nm_rsize, nmp->nm_wsize);
fa4905b1
A
223 if (iosize < PAGE_SIZE)
224 iosize = PAGE_SIZE;
de355530 225 return (trunc_page(iosize));
1c79356b
A
226}
227
228static void nfs_convert_oargs(args,oargs)
229 struct nfs_args *args;
230 struct onfs_args *oargs;
231{
232 args->version = NFS_ARGSVERSION;
233 args->addr = oargs->addr;
234 args->addrlen = oargs->addrlen;
235 args->sotype = oargs->sotype;
236 args->proto = oargs->proto;
237 args->fh = oargs->fh;
238 args->fhsize = oargs->fhsize;
239 args->flags = oargs->flags;
240 args->wsize = oargs->wsize;
241 args->rsize = oargs->rsize;
242 args->readdirsize = oargs->readdirsize;
243 args->timeo = oargs->timeo;
244 args->retrans = oargs->retrans;
245 args->maxgrouplist = oargs->maxgrouplist;
246 args->readahead = oargs->readahead;
247 args->leaseterm = oargs->leaseterm;
248 args->deadthresh = oargs->deadthresh;
249 args->hostname = oargs->hostname;
250}
251
252/*
253 * nfs statfs call
254 */
255int
256nfs_statfs(mp, sbp, p)
257 struct mount *mp;
258 register struct statfs *sbp;
259 struct proc *p;
260{
261 register struct vnode *vp;
262 register struct nfs_statfs *sfp;
263 register caddr_t cp;
264 register u_long *tl;
265 register long t1, t2;
266 caddr_t bpos, dpos, cp2;
267 struct nfsmount *nmp = VFSTONFS(mp);
268 int error = 0, v3 = (nmp->nm_flag & NFSMNT_NFSV3), retattr;
269 struct mbuf *mreq, *mrep, *md, *mb, *mb2;
270 struct ucred *cred;
271 u_quad_t tquad;
272 extern int nfs_mount_type;
fa4905b1 273 u_int64_t xid;
1c79356b
A
274
275#ifndef nolint
276 sfp = (struct nfs_statfs *)0;
277#endif
278 vp = nmp->nm_dvp;
fa4905b1
A
279 if (error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p))
280 return(error);
1c79356b
A
281 cred = crget();
282 cred->cr_ngroups = 1;
283 if (v3 && (nmp->nm_flag & NFSMNT_GOTFSINFO) == 0)
284 (void)nfs_fsinfo(nmp, vp, cred, p);
285 nfsstats.rpccnt[NFSPROC_FSSTAT]++;
286 nfsm_reqhead(vp, NFSPROC_FSSTAT, NFSX_FH(v3));
287 nfsm_fhtom(vp, v3);
fa4905b1 288 nfsm_request(vp, NFSPROC_FSSTAT, p, cred, &xid);
1c79356b 289 if (v3)
fa4905b1 290 nfsm_postop_attr(vp, retattr, &xid);
1c79356b
A
291 nfsm_dissect(sfp, struct nfs_statfs *, NFSX_STATFS(v3));
292
293/* XXX CSM 12/2/97 Cleanup when/if we integrate FreeBSD mount.h */
294#ifdef notyet
295 sbp->f_type = MOUNT_NFS;
296#else
297 sbp->f_type = nfs_mount_type;
298#endif
299 sbp->f_flags = nmp->nm_flag;
300 sbp->f_iosize = nfs_iosize(nmp);
301 if (v3) {
302 sbp->f_bsize = NFS_FABLKSIZE;
303 fxdr_hyper(&sfp->sf_tbytes, &tquad);
304 sbp->f_blocks = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE));
305 fxdr_hyper(&sfp->sf_fbytes, &tquad);
306 sbp->f_bfree = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE));
307 fxdr_hyper(&sfp->sf_abytes, &tquad);
308 sbp->f_bavail = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE));
309 sbp->f_files = (fxdr_unsigned(long, sfp->sf_tfiles.nfsuquad[1])
310 & 0x7fffffff);
311 sbp->f_ffree = (fxdr_unsigned(long, sfp->sf_ffiles.nfsuquad[1])
312 & 0x7fffffff);
313 } else {
314 sbp->f_bsize = fxdr_unsigned(long, sfp->sf_bsize);
315 sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks);
316 sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree);
317 sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail);
318 sbp->f_files = 0;
319 sbp->f_ffree = 0;
320 }
321 if (sbp != &mp->mnt_stat) {
322 bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
323 bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
324 }
325 nfsm_reqdone;
326 VOP_UNLOCK(vp, 0, p);
327 crfree(cred);
328 return (error);
329}
330
331/*
332 * nfs version 3 fsinfo rpc call
333 */
334int
335nfs_fsinfo(nmp, vp, cred, p)
336 register struct nfsmount *nmp;
337 register struct vnode *vp;
338 struct ucred *cred;
339 struct proc *p;
340{
341 register struct nfsv3_fsinfo *fsp;
342 register caddr_t cp;
343 register long t1, t2;
344 register u_long *tl, pref, max;
345 caddr_t bpos, dpos, cp2;
346 int error = 0, retattr;
347 struct mbuf *mreq, *mrep, *md, *mb, *mb2;
fa4905b1 348 u_int64_t xid;
1c79356b
A
349
350 nfsstats.rpccnt[NFSPROC_FSINFO]++;
351 nfsm_reqhead(vp, NFSPROC_FSINFO, NFSX_FH(1));
352 nfsm_fhtom(vp, 1);
fa4905b1
A
353 nfsm_request(vp, NFSPROC_FSINFO, p, cred, &xid);
354 nfsm_postop_attr(vp, retattr, &xid);
1c79356b
A
355 if (!error) {
356 nfsm_dissect(fsp, struct nfsv3_fsinfo *, NFSX_V3FSINFO);
357 pref = fxdr_unsigned(u_long, fsp->fs_wtpref);
358 if (pref < nmp->nm_wsize)
359 nmp->nm_wsize = (pref + NFS_FABLKSIZE - 1) &
360 ~(NFS_FABLKSIZE - 1);
361 max = fxdr_unsigned(u_long, fsp->fs_wtmax);
362 if (max < nmp->nm_wsize) {
363 nmp->nm_wsize = max & ~(NFS_FABLKSIZE - 1);
364 if (nmp->nm_wsize == 0)
365 nmp->nm_wsize = max;
366 }
367 pref = fxdr_unsigned(u_long, fsp->fs_rtpref);
368 if (pref < nmp->nm_rsize)
369 nmp->nm_rsize = (pref + NFS_FABLKSIZE - 1) &
370 ~(NFS_FABLKSIZE - 1);
371 max = fxdr_unsigned(u_long, fsp->fs_rtmax);
372 if (max < nmp->nm_rsize) {
373 nmp->nm_rsize = max & ~(NFS_FABLKSIZE - 1);
374 if (nmp->nm_rsize == 0)
375 nmp->nm_rsize = max;
376 }
377 pref = fxdr_unsigned(u_long, fsp->fs_dtpref);
378 if (pref < nmp->nm_readdirsize)
379 nmp->nm_readdirsize = pref;
380 if (max < nmp->nm_readdirsize) {
381 nmp->nm_readdirsize = max;
382 }
383 nmp->nm_flag |= NFSMNT_GOTFSINFO;
384 }
385 nfsm_reqdone;
386 return (error);
387}
388
389/*
390 * Mount a remote root fs via. nfs. This depends on the info in the
391 * nfs_diskless structure that has been filled in properly by some primary
392 * bootstrap.
393 * It goes something like this:
394 * - do enough of "ifconfig" by calling ifioctl() so that the system
395 * can talk to the server
396 * - If nfs_diskless.mygateway is filled in, use that address as
397 * a default gateway.
398 * - hand craft the swap nfs vnode hanging off a fake mount point
399 * if swdevt[0].sw_dev == NODEV
400 * - build the rootfs mount point and call mountnfs() to do the rest.
401 */
402int
403nfs_mountroot()
404{
405 struct nfs_diskless nd;
406 struct vattr attr;
407 struct mount *mp;
408 struct vnode *vp;
409 struct proc *procp;
410 long n;
411 int error;
412#if !defined(NO_MOUNT_PRIVATE)
413 struct mount *mppriv;
414 struct vnode *vppriv;
415#endif /* NO_MOUNT_PRIVATE */
416
417 procp = current_proc(); /* XXX */
418
419 /*
420 * Call nfs_boot_init() to fill in the nfs_diskless struct.
9bccf70c
A
421 * Note: networking must already have been configured before
422 * we're called.
1c79356b
A
423 */
424 bzero((caddr_t) &nd, sizeof(nd));
9bccf70c
A
425 error = nfs_boot_init(&nd, procp);
426 if (error) {
427 panic("nfs_boot_init failed with %d\n", error);
428 }
1c79356b
A
429
430 /*
431 * Create the root mount point.
432 */
433#if !defined(NO_MOUNT_PRIVATE)
434 if ((error = nfs_mount_diskless(&nd.nd_root, "/", MNT_RDONLY, &vp, &mp))) {
435#else
436 if (error = nfs_mount_diskless(&nd.nd_root, "/", NULL, &vp, &mp)) {
437#endif /* NO_MOUNT_PRIVATE */
de355530 438 panic("nfs_mount_diskless failed with %d\n", error);
1c79356b
A
439 }
440 printf("root on %s\n", (char *)&nd.nd_root.ndm_host);
441
442 simple_lock(&mountlist_slock);
443 CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
444 simple_unlock(&mountlist_slock);
445 vfs_unbusy(mp, procp);
446 rootvp = vp;
447
448#if !defined(NO_MOUNT_PRIVATE)
449 if (nd.nd_private.ndm_saddr.sin_addr.s_addr) {
450 error = nfs_mount_diskless_private(&nd.nd_private, "/private",
451 NULL, &vppriv, &mppriv);
9bccf70c 452 if (error) {
de355530 453 panic("nfs_mount_diskless failed with %d\n", error);
9bccf70c 454 }
1c79356b
A
455 printf("private on %s\n", (char *)&nd.nd_private.ndm_host);
456
457 simple_lock(&mountlist_slock);
458 CIRCLEQ_INSERT_TAIL(&mountlist, mppriv, mnt_list);
459 simple_unlock(&mountlist_slock);
460 vfs_unbusy(mppriv, procp);
461 }
462
463#endif /* NO_MOUNT_PRIVATE */
464
465 /* Get root attributes (for the time). */
466 error = VOP_GETATTR(vp, &attr, procp->p_ucred, procp);
467 if (error) panic("nfs_mountroot: getattr for root");
468 n = attr.va_mtime.tv_sec;
469 inittodr(n);
470 return (0);
471}
472
473/*
474 * Internal version of mount system call for diskless setup.
475 */
476static int
477nfs_mount_diskless(ndmntp, mntname, mntflag, vpp, mpp)
478 struct nfs_dlmount *ndmntp;
479 char *mntname;
480 int mntflag;
481 struct vnode **vpp;
482 struct mount **mpp;
483{
484 struct nfs_args args;
485 struct mount *mp;
486 struct mbuf *m;
487 int error;
488 struct proc *procp;
489
490 procp = current_proc(); /* XXX */
491
492 if ((error = vfs_rootmountalloc("nfs", ndmntp->ndm_host, &mp))) {
493 printf("nfs_mountroot: NFS not configured");
494 return (error);
495 }
496 mp->mnt_flag = mntflag;
497
498 /* Initialize mount args. */
499 bzero((caddr_t) &args, sizeof(args));
500 args.addr = (struct sockaddr *)&ndmntp->ndm_saddr;
501 args.addrlen = args.addr->sa_len;
502 args.sotype = SOCK_DGRAM;
503 args.fh = ndmntp->ndm_fh;
de355530 504 args.fhsize = NFSX_V2FH; /* need to try v3, then v2 */
1c79356b
A
505 args.hostname = ndmntp->ndm_host;
506 args.flags = NFSMNT_RESVPORT;
507
508 MGET(m, M_DONTWAIT, MT_SONAME);
509 bcopy((caddr_t)args.addr, mtod(m, caddr_t),
510 (m->m_len = args.addr->sa_len));
511 if ((error = mountnfs(&args, mp, m, mntname, args.hostname, vpp))) {
512 printf("nfs_mountroot: mount %s failed: %d", mntname, error);
513 mp->mnt_vfc->vfc_refcount--;
514 vfs_unbusy(mp, procp);
515 _FREE_ZONE(mp, sizeof (struct mount), M_MOUNT);
516 return (error);
517 }
518#if 0 /* Causes incorrect reporting of "mounted on" */
519 (void) copystr(args.hostname, mp->mnt_stat.f_mntonname, MNAMELEN - 1, 0);
520#endif /* 0 */
521 *mpp = mp;
522 return (0);
523}
524
525#if !defined(NO_MOUNT_PRIVATE)
526/*
527 * Internal version of mount system call to mount "/private"
528 * separately in diskless setup
529 */
530static int
531nfs_mount_diskless_private(ndmntp, mntname, mntflag, vpp, mpp)
532 struct nfs_dlmount *ndmntp;
533 char *mntname;
534 int mntflag;
535 struct vnode **vpp;
536 struct mount **mpp;
537{
538 struct nfs_args args;
539 struct mount *mp;
540 struct mbuf *m;
541 int error;
542 struct proc *procp;
543 struct vfsconf *vfsp;
544 struct nameidata nd;
545 struct vnode *vp;
546
547 procp = current_proc(); /* XXX */
548
549 {
550 /*
551 * mimic main()!. Temporarily set up rootvnode and other stuff so
552 * that namei works. Need to undo this because main() does it, too
553 */
554 struct filedesc *fdp; /* pointer to file descriptor state */
555 fdp = procp->p_fd;
556 mountlist.cqh_first->mnt_flag |= MNT_ROOTFS;
557
558 /* Get the vnode for '/'. Set fdp->fd_cdir to reference it. */
559 if (VFS_ROOT(mountlist.cqh_first, &rootvnode))
560 panic("cannot find root vnode");
fa4905b1 561 VREF(rootvnode);
1c79356b 562 fdp->fd_cdir = rootvnode;
1c79356b
A
563 VOP_UNLOCK(rootvnode, 0, procp);
564 fdp->fd_rdir = NULL;
565 }
566
567 /*
568 * Get vnode to be covered
569 */
570 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE,
571 mntname, procp);
572 if ((error = namei(&nd))) {
573 printf("nfs_mountroot: private namei failed!");
574 return (error);
575 }
576 {
577 /* undo VREF in mimic main()! */
578 vrele(rootvnode);
579 }
580 vp = nd.ni_vp;
581 if ((error = vinvalbuf(vp, V_SAVE, procp->p_ucred, procp, 0, 0))) {
582 vput(vp);
583 return (error);
584 }
585 if (vp->v_type != VDIR) {
586 vput(vp);
587 return (ENOTDIR);
588 }
589 for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next)
590 if (!strcmp(vfsp->vfc_name, "nfs"))
591 break;
592 if (vfsp == NULL) {
593 printf("nfs_mountroot: private NFS not configured");
594 vput(vp);
595 return (ENODEV);
596 }
597 if (vp->v_mountedhere != NULL) {
598 vput(vp);
599 return (EBUSY);
600 }
601
602 /*
603 * Allocate and initialize the filesystem.
604 */
605 mp = _MALLOC_ZONE((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK);
606 bzero((char *)mp, (u_long)sizeof(struct mount));
0b4e3aa0
A
607
608 /* Initialize the default IO constraints */
609 mp->mnt_maxreadcnt = mp->mnt_maxwritecnt = MAXPHYS;
610 mp->mnt_segreadcnt = mp->mnt_segwritecnt = 32;
611
1c79356b
A
612 lockinit(&mp->mnt_lock, PVFS, "vfslock", 0, 0);
613 (void)vfs_busy(mp, LK_NOWAIT, 0, procp);
614 LIST_INIT(&mp->mnt_vnodelist);
615 mp->mnt_op = vfsp->vfc_vfsops;
616 mp->mnt_vfc = vfsp;
617 vfsp->vfc_refcount++;
618 mp->mnt_stat.f_type = vfsp->vfc_typenum;
619 mp->mnt_flag = mntflag;
620 mp->mnt_flag |= vfsp->vfc_flags & MNT_VISFLAGMASK;
621 strncpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN);
622 vp->v_mountedhere = mp;
623 mp->mnt_vnodecovered = vp;
624 mp->mnt_stat.f_owner = procp->p_ucred->cr_uid;
625 (void) copystr(mntname, mp->mnt_stat.f_mntonname, MNAMELEN - 1, 0);
626 (void) copystr(ndmntp->ndm_host, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, 0);
627
628 /* Initialize mount args. */
629 bzero((caddr_t) &args, sizeof(args));
630 args.addr = (struct sockaddr *)&ndmntp->ndm_saddr;
631 args.addrlen = args.addr->sa_len;
632 args.sotype = SOCK_DGRAM;
633 args.fh = ndmntp->ndm_fh;
de355530 634 args.fhsize = NFSX_V2FH;
1c79356b
A
635 args.hostname = ndmntp->ndm_host;
636 args.flags = NFSMNT_RESVPORT;
637
638 MGET(m, M_DONTWAIT, MT_SONAME);
639 bcopy((caddr_t)args.addr, mtod(m, caddr_t),
640 (m->m_len = args.addr->sa_len));
641 if ((error = mountnfs(&args, mp, m, mntname, args.hostname, &vp))) {
642 printf("nfs_mountroot: mount %s failed: %d", mntname, error);
643 mp->mnt_vfc->vfc_refcount--;
644 vfs_unbusy(mp, procp);
645 _FREE_ZONE(mp, sizeof (struct mount), M_MOUNT);
646 return (error);
647 }
648
649 *mpp = mp;
650 *vpp = vp;
651 return (0);
652}
653#endif /* NO_MOUNT_PRIVATE */
654
655/*
656 * VFS Operations.
657 *
658 * mount system call
659 * It seems a bit dumb to copyinstr() the host and path here and then
660 * bcopy() them in mountnfs(), but I wanted to detect errors before
661 * doing the sockargs() call because sockargs() allocates an mbuf and
662 * an error after that means that I have to release the mbuf.
663 */
664/* ARGSUSED */
665static int
666nfs_mount(mp, path, data, ndp, p)
667 struct mount *mp;
668 char *path;
669 caddr_t data;
670 struct nameidata *ndp;
671 struct proc *p;
672{
673 int error;
674 struct nfs_args args;
675 struct mbuf *nam;
676 struct vnode *vp;
677 char pth[MNAMELEN], hst[MNAMELEN];
678 u_int len;
679 u_char nfh[NFSX_V3FHMAX];
680
681 error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args));
682 if (error)
683 return (error);
684 if (args.version != NFS_ARGSVERSION) {
685#ifndef NO_COMPAT_PRELITE2
686 /*
687 * If the argument version is unknown, then assume the
688 * caller is a pre-lite2 4.4BSD client and convert its
689 * arguments.
690 */
691 struct onfs_args oargs;
692 error = copyin(data, (caddr_t)&oargs, sizeof (struct onfs_args));
693 if (error)
694 return (error);
695 nfs_convert_oargs(&args,&oargs);
696#else /* NO_COMPAT_PRELITE2 */
697 return (EPROGMISMATCH);
698#endif /* !NO_COMPAT_PRELITE2 */
699 }
700 if (args.fhsize > NFSX_V3FHMAX)
701 return (EINVAL);
702 error = copyin((caddr_t)args.fh, (caddr_t)nfh, args.fhsize);
703 if (error)
704 return (error);
705 error = copyinstr(path, pth, MNAMELEN-1, &len);
706 if (error)
707 return (error);
708 bzero(&pth[len], MNAMELEN - len);
709 error = copyinstr(args.hostname, hst, MNAMELEN-1, &len);
710 if (error)
711 return (error);
712 bzero(&hst[len], MNAMELEN - len);
713 /* sockargs() call must be after above copyin() calls */
714 error = sockargs(&nam, (caddr_t)args.addr, args.addrlen, MT_SONAME);
715 if (error)
716 return (error);
717 args.fh = nfh;
718 error = mountnfs(&args, mp, nam, pth, hst, &vp);
719 return (error);
720}
721
722/*
723 * Common code for mount and mountroot
724 */
725static int
726mountnfs(argp, mp, nam, pth, hst, vpp)
727 register struct nfs_args *argp;
728 register struct mount *mp;
729 struct mbuf *nam;
730 char *pth, *hst;
731 struct vnode **vpp;
732{
733 register struct nfsmount *nmp;
734 struct nfsnode *np;
735 int error, maxio;
736 struct vattr attrs;
737 struct proc *curproc;
738
739 /*
740 * turning off NQNFS until we have further testing
741 * with UBC changes, in particular, nfs_pagein and nfs_pageout.
742 * Those have NQNFS defined out in conjunction with this
743 * returning an error. Remove when fully tested.
744 */
745 if (argp->flags & NFSMNT_NQNFS) {
746 error = NFSERR_NOTSUPP;
747 goto bad2;
748 }
749
750 if (mp->mnt_flag & MNT_UPDATE) {
751 nmp = VFSTONFS(mp);
752 /* update paths, file handles, etc, here XXX */
753 m_freem(nam);
754 return (0);
755 } else {
756 MALLOC_ZONE(nmp, struct nfsmount *,
757 sizeof (struct nfsmount), M_NFSMNT, M_WAITOK);
758 bzero((caddr_t)nmp, sizeof (struct nfsmount));
759 TAILQ_INIT(&nmp->nm_uidlruhead);
760 TAILQ_INIT(&nmp->nm_bufq);
761 mp->mnt_data = (qaddr_t)nmp;
762 }
763 vfs_getnewfsid(mp);
764 nmp->nm_mountp = mp;
765 nmp->nm_flag = argp->flags;
766 if (nmp->nm_flag & NFSMNT_NQNFS)
767 /*
768 * We have to set mnt_maxsymlink to a non-zero value so
769 * that COMPAT_43 routines will know that we are setting
770 * the d_type field in directories (and can zero it for
771 * unsuspecting binaries).
772 */
773 mp->mnt_maxsymlinklen = 1;
774 nmp->nm_timeo = NFS_TIMEO;
775 nmp->nm_retry = NFS_RETRANS;
776 nmp->nm_wsize = NFS_WSIZE;
777 nmp->nm_rsize = NFS_RSIZE;
778 nmp->nm_readdirsize = NFS_READDIRSIZE;
779 nmp->nm_numgrps = NFS_MAXGRPS;
780 nmp->nm_readahead = NFS_DEFRAHEAD;
781 nmp->nm_leaseterm = NQ_DEFLEASE;
782 nmp->nm_deadthresh = NQ_DEADTHRESH;
783 CIRCLEQ_INIT(&nmp->nm_timerhead);
784 nmp->nm_inprog = NULLVP;
785 bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
786 bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN);
787 nmp->nm_nam = nam;
788
789 /*
790 * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
791 * no sense in that context.
792 */
793 if (argp->sotype == SOCK_STREAM)
794 argp->flags &= ~NFSMNT_NOCONN;
795
796 if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
797 nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
798 if (nmp->nm_timeo < NFS_MINTIMEO)
799 nmp->nm_timeo = NFS_MINTIMEO;
800 else if (nmp->nm_timeo > NFS_MAXTIMEO)
801 nmp->nm_timeo = NFS_MAXTIMEO;
802 }
803
804 if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
805 nmp->nm_retry = argp->retrans;
806 if (nmp->nm_retry > NFS_MAXREXMIT)
807 nmp->nm_retry = NFS_MAXREXMIT;
808 }
809
810 if (argp->flags & NFSMNT_NFSV3) {
811 if (argp->sotype == SOCK_DGRAM)
812 maxio = NFS_MAXDGRAMDATA;
813 else
814 maxio = NFS_MAXDATA;
815 } else
816 maxio = NFS_V2MAXDATA;
817
818 if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
819 nmp->nm_wsize = argp->wsize;
820 /* Round down to multiple of blocksize */
821 nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
822 if (nmp->nm_wsize <= 0)
823 nmp->nm_wsize = NFS_FABLKSIZE;
824 }
825 if (nmp->nm_wsize > maxio)
826 nmp->nm_wsize = maxio;
827 if (nmp->nm_wsize > MAXBSIZE)
828 nmp->nm_wsize = MAXBSIZE;
829
830 if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
831 nmp->nm_rsize = argp->rsize;
832 /* Round down to multiple of blocksize */
833 nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
834 if (nmp->nm_rsize <= 0)
835 nmp->nm_rsize = NFS_FABLKSIZE;
836 }
837 if (nmp->nm_rsize > maxio)
838 nmp->nm_rsize = maxio;
839 if (nmp->nm_rsize > MAXBSIZE)
840 nmp->nm_rsize = MAXBSIZE;
841
842 if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
843 nmp->nm_readdirsize = argp->readdirsize;
844 }
845 if (nmp->nm_readdirsize > maxio)
846 nmp->nm_readdirsize = maxio;
847 if (nmp->nm_readdirsize > nmp->nm_rsize)
848 nmp->nm_readdirsize = nmp->nm_rsize;
849
850 if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 &&
851 argp->maxgrouplist <= NFS_MAXGRPS)
852 nmp->nm_numgrps = argp->maxgrouplist;
853 if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0 &&
854 argp->readahead <= NFS_MAXRAHEAD)
855 nmp->nm_readahead = argp->readahead;
856 if ((argp->flags & NFSMNT_LEASETERM) && argp->leaseterm >= 2 &&
857 argp->leaseterm <= NQ_MAXLEASE)
858 nmp->nm_leaseterm = argp->leaseterm;
859 if ((argp->flags & NFSMNT_DEADTHRESH) && argp->deadthresh >= 1 &&
860 argp->deadthresh <= NQ_NEVERDEAD)
861 nmp->nm_deadthresh = argp->deadthresh;
862 /* Set up the sockets and per-host congestion */
863 nmp->nm_sotype = argp->sotype;
864 nmp->nm_soproto = argp->proto;
865
866 /*
867 * For Connection based sockets (TCP,...) defer the connect until
868 * the first request, in case the server is not responding.
869 */
870 if (nmp->nm_sotype == SOCK_DGRAM &&
871 (error = nfs_connect(nmp, (struct nfsreq *)0)))
872 goto bad;
873
874 /*
875 * This is silly, but it has to be set so that vinifod() works.
876 * We do not want to do an nfs_statfs() here since we can get
877 * stuck on a dead server and we are holding a lock on the mount
878 * point.
879 */
880 mp->mnt_stat.f_iosize = nfs_iosize(nmp);
881 /*
882 * A reference count is needed on the nfsnode representing the
883 * remote root. If this object is not persistent, then backward
884 * traversals of the mount point (i.e. "..") will not work if
885 * the nfsnode gets flushed out of the cache. UFS does not have
886 * this problem, because one can identify root inodes by their
887 * number == ROOTINO (2).
888 */
889 error = nfs_nget(mp, (nfsfh_t *)argp->fh, argp->fhsize, &np);
890 if (error)
891 goto bad;
892
893 /*
894 * save this vnode pointer. That way nfs_unmount()
895 * does not need to call nfs_net() just get it to drop
896 * this vnode reference.
897 */
898 nmp->nm_dvp = *vpp = NFSTOV(np);
899
900 /*
901 * Get file attributes for the mountpoint. This has the side
902 * effect of filling in (*vpp)->v_type with the correct value.
903 */
904 curproc = current_proc();
905 VOP_GETATTR(*vpp, &attrs, curproc->p_ucred, curproc);
906
907 /*
908 * Lose the lock but keep the ref.
909 */
910 VOP_UNLOCK(*vpp, 0, curproc);
911
912 return (0);
913bad:
914 nfs_disconnect(nmp);
915 _FREE_ZONE((caddr_t)nmp, sizeof (struct nfsmount), M_NFSMNT);
916bad2:
917 m_freem(nam);
918 return (error);
919}
920
921
922/*
923 * unmount system call
924 */
925static int
926nfs_unmount(mp, mntflags, p)
927 struct mount *mp;
928 int mntflags;
929 struct proc *p;
930{
931 register struct nfsmount *nmp;
932 struct vnode *vp;
933 int error, flags = 0;
934
935 if (mntflags & MNT_FORCE)
936 flags |= FORCECLOSE;
937 nmp = VFSTONFS(mp);
938 /*
939 * Goes something like this..
1c79356b 940 * - Call vflush() to clear out vnodes for this file system,
0b4e3aa0
A
941 * except for the swap files. Deal with them in 2nd pass.
942 * It will do vgone making the vnode VBAD at that time.
1c79356b
A
943 * - Decrement reference on the vnode representing remote root.
944 * - Close the socket
945 * - Free up the data structures
1c79356b
A
946 */
947 vp = nmp->nm_dvp;
0b4e3aa0 948
1c79356b
A
949 /*
950 * Must handshake with nqnfs_clientd() if it is active.
951 */
952 nmp->nm_flag |= NFSMNT_DISMINPROG;
953 while (nmp->nm_inprog != NULLVP)
954 (void) tsleep((caddr_t)&lbolt, PSOCK, "nfsdism", 0);
0b4e3aa0
A
955 /*
956 * vflush will check for busy vnodes on mountpoint.
957 * Will do the right thing for MNT_FORCE. That is, we should
958 * not get EBUSY back.
959 */
960 error = vflush(mp, vp, SKIPSWAP | flags);
961 if (mntflags & MNT_FORCE)
fa4905b1 962 error = vflush(mp, NULLVP, flags); /* locks vp in the process */
0b4e3aa0
A
963 else {
964 if (vp->v_usecount > 1) {
0b4e3aa0
A
965 nmp->nm_flag &= ~NFSMNT_DISMINPROG;
966 return (EBUSY);
967 }
968 error = vflush(mp, vp, flags);
969 }
970
1c79356b 971 if (error) {
1c79356b
A
972 nmp->nm_flag &= ~NFSMNT_DISMINPROG;
973 return (error);
974 }
975
976 /*
977 * We are now committed to the unmount.
978 * For NQNFS, let the server daemon free the nfsmount structure.
979 */
980 if (nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB))
981 nmp->nm_flag |= NFSMNT_DISMNT;
982
983 /*
984 * Release the root vnode reference held by mountnfs()
fa4905b1
A
985 * vflush did the vgone for us when we didn't skip over
986 * it in the MNT_FORCE case. (Thus vp can't be locked when
987 * called vflush in non-skip vp case.)
1c79356b 988 */
fa4905b1 989 vrele(vp);
0b4e3aa0
A
990 if (!(mntflags & MNT_FORCE))
991 vgone(vp);
992 mp->mnt_data = 0; /* don't want to end up using stale vp */
1c79356b
A
993 nfs_disconnect(nmp);
994 m_freem(nmp->nm_nam);
995
996 if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB)) == 0) {
997 register struct nfsreq *rp;
998 /*
999 * Loop through outstanding request list and remove dangling
1000 * references to defunct nfsmount struct
1001 */
1002#if NFSDIAG && 0
1003 if (hw_atomic_add(&nfsreqqusers, 1) != 1)
1004 nfsatompanic("unmount add");
1005 nfsbtlen = backtrace(&nfsbt, sizeof(nfsbt));
1006 nfsbtcpu = cpu_number();
1007 nfsbtthread = (int)(current_thread());
1008#endif
1009
1010 for (rp = nfs_reqq.tqh_first; rp; rp = rp->r_chain.tqe_next)
1011 if (rp->r_nmp == nmp)
1012 rp->r_nmp = (struct nfsmount *)0;
1013#if NFSDIAG && 0
1014 if (hw_atomic_sub(&nfsreqqusers, 1) != 0)
1015 nfsatompanic("unmount sub");
1016#endif
1017 _FREE_ZONE((caddr_t)nmp, sizeof (struct nfsmount), M_NFSMNT);
1018 }
1019 return (0);
1020}
1021
1022/*
1023 * Return root of a filesystem
1024 */
1025static int
1026nfs_root(mp, vpp)
1027 struct mount *mp;
1028 struct vnode **vpp;
1029{
1030 register struct vnode *vp;
1031 struct nfsmount *nmp;
1032 int error;
1033
1034 nmp = VFSTONFS(mp);
1035 vp = nmp->nm_dvp;
1036 error = vget(vp, LK_EXCLUSIVE, current_proc());
1037 if (error)
1038 return (error);
1039 if (vp->v_type == VNON)
1040 vp->v_type = VDIR;
1041 vp->v_flag |= VROOT;
1042 *vpp = vp;
1043 return (0);
1044}
1045
1046extern int syncprt;
1047
1048/*
1049 * Flush out the buffer cache
1050 */
1051/* ARGSUSED */
1052static int
1053nfs_sync(mp, waitfor, cred, p)
1054 struct mount *mp;
1055 int waitfor;
1056 struct ucred *cred;
1057 struct proc *p;
1058{
1059 register struct vnode *vp;
1060 int error, allerror = 0;
1061
1062 /*
1063 * Force stale buffer cache information to be flushed.
1064 */
1065loop:
1066 for (vp = mp->mnt_vnodelist.lh_first;
1067 vp != NULL;
1068 vp = vp->v_mntvnodes.le_next) {
fa4905b1 1069 int didhold = 0;
1c79356b
A
1070 /*
1071 * If the vnode that we are about to sync is no longer
1072 * associated with this mount point, start over.
1073 */
1074 if (vp->v_mount != mp)
1075 goto loop;
1076 if (VOP_ISLOCKED(vp) || vp->v_dirtyblkhd.lh_first == NULL)
1077 continue;
1078 if (vget(vp, LK_EXCLUSIVE, p))
1079 goto loop;
fa4905b1 1080 didhold = ubc_hold(vp);
1c79356b
A
1081 error = VOP_FSYNC(vp, cred, waitfor, p);
1082 if (error)
1083 allerror = error;
fa4905b1
A
1084 VOP_UNLOCK(vp, 0, p);
1085 if (didhold)
1086 ubc_rele(vp);
1087 vrele(vp);
1c79356b
A
1088 }
1089 return (allerror);
1090}
1091
1092/*
1093 * NFS flat namespace lookup.
1094 * Currently unsupported.
1095 */
1096/* ARGSUSED */
1097static int
1098nfs_vget(mp, ino, vpp)
1099 struct mount *mp;
1100 ino_t ino;
1101 struct vnode **vpp;
1102{
1103
1104 return (EOPNOTSUPP);
1105}
1106
1107/*
1108 * At this point, this should never happen
1109 */
1110/* ARGSUSED */
1111static int
1112nfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
1113 register struct mount *mp;
1114 struct fid *fhp;
1115 struct mbuf *nam;
1116 struct vnode **vpp;
1117 int *exflagsp;
1118 struct ucred **credanonp;
1119{
1120
1121 return (EINVAL);
1122}
1123
1124/*
1125 * Vnode pointer to File handle, should never happen either
1126 */
1127/* ARGSUSED */
1128static int
1129nfs_vptofh(vp, fhp)
1130 struct vnode *vp;
1131 struct fid *fhp;
1132{
1133
1134 return (EINVAL);
1135}
1136
1137/*
1138 * Vfs start routine, a no-op.
1139 */
1140/* ARGSUSED */
1141static int
1142nfs_start(mp, flags, p)
1143 struct mount *mp;
1144 int flags;
1145 struct proc *p;
1146{
1147
1148 return (0);
1149}
1150
1151/*
1152 * Do operations associated with quotas, not supported
1153 */
1154/* ARGSUSED */
1155static int
1156nfs_quotactl(mp, cmd, uid, arg, p)
1157 struct mount *mp;
1158 int cmd;
1159 uid_t uid;
1160 caddr_t arg;
1161 struct proc *p;
1162{
1163
1164 return (EOPNOTSUPP);
1165}
1166
1167/*
1168 * Do that sysctl thang...
1169 */
1170static int
1171nfs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
1172 size_t newlen, struct proc *p)
1173{
1174 int rv;
1175
1176 /*
1177 * All names at this level are terminal.
1178 */
1179 if(namelen > 1)
1180 return ENOTDIR; /* overloaded */
1181
1182 switch(name[0]) {
1183 case NFS_NFSSTATS:
1184 if(!oldp) {
1185 *oldlenp = sizeof nfsstats;
1186 return 0;
1187 }
1188
1189 if(*oldlenp < sizeof nfsstats) {
1190 *oldlenp = sizeof nfsstats;
1191 return ENOMEM;
1192 }
1193
1194 rv = copyout(&nfsstats, oldp, sizeof nfsstats);
1195 if(rv) return rv;
1196
1197 if(newp && newlen != sizeof nfsstats)
1198 return EINVAL;
1199
1200 if(newp) {
1201 return copyin(newp, &nfsstats, sizeof nfsstats);
1202 }
1203 return 0;
1204
1205 default:
1206 return EOPNOTSUPP;
1207 }
1208}
1209