Commit | Line | Data |
---|---|---|
a2907882 | 1 | /* |
4acac3d6 | 2 | * Copyright (c) 1989, 1993, 1995 |
99315dca | 3 | * The Regents of the University of California. All rights reserved. |
a2907882 KM |
4 | * |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Rick Macklem at The University of Guelph. | |
7 | * | |
ad787160 C |
8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
16 | * 3. All advertising materials mentioning features or use of this software | |
17 | * must display the following acknowledgement: | |
18 | * This product includes software developed by the University of | |
19 | * California, Berkeley and its contributors. | |
20 | * 4. Neither the name of the University nor the names of its contributors | |
21 | * may be used to endorse or promote products derived from this software | |
22 | * without specific prior written permission. | |
a2907882 | 23 | * |
ad787160 C |
24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
34 | * SUCH DAMAGE. | |
35 | * | |
fd88f5c5 | 36 | * @(#)nfs_vfsops.c 8.12 (Berkeley) 5/20/95 |
a2907882 KM |
37 | */ |
38 | ||
5548a02f KB |
39 | #include <sys/param.h> |
40 | #include <sys/conf.h> | |
41 | #include <sys/ioctl.h> | |
42 | #include <sys/signal.h> | |
43 | #include <sys/proc.h> | |
44 | #include <sys/namei.h> | |
45 | #include <sys/vnode.h> | |
46 | #include <sys/kernel.h> | |
47 | #include <sys/mount.h> | |
48 | #include <sys/buf.h> | |
49 | #include <sys/mbuf.h> | |
50 | #include <sys/socket.h> | |
4acac3d6 | 51 | #include <sys/socketvar.h> |
5548a02f | 52 | #include <sys/systm.h> |
058dee65 | 53 | |
5548a02f KB |
54 | #include <net/if.h> |
55 | #include <net/route.h> | |
56 | #include <netinet/in.h> | |
058dee65 | 57 | |
5548a02f | 58 | #include <nfs/rpcv2.h> |
4acac3d6 | 59 | #include <nfs/nfsproto.h> |
5548a02f | 60 | #include <nfs/nfsnode.h> |
5548a02f | 61 | #include <nfs/nfs.h> |
4acac3d6 | 62 | #include <nfs/nfsmount.h> |
5548a02f KB |
63 | #include <nfs/xdr_subs.h> |
64 | #include <nfs/nfsm_subs.h> | |
65 | #include <nfs/nfsdiskless.h> | |
66 | #include <nfs/nqnfs.h> | |
a2907882 | 67 | |
4acac3d6 KM |
68 | struct nfsstats nfsstats; |
69 | static int nfs_sysctl(int *, u_int, void *, size_t *, void *, size_t, | |
70 | struct proc *); | |
71 | extern int nfs_ticks; | |
72 | ||
a2907882 KM |
73 | /* |
74 | * nfs vfs operations. | |
75 | */ | |
a2907882 KM |
76 | struct vfsops nfs_vfsops = { |
77 | nfs_mount, | |
b3226026 | 78 | nfs_start, |
a2907882 KM |
79 | nfs_unmount, |
80 | nfs_root, | |
56cf2c46 | 81 | nfs_quotactl, |
a2907882 KM |
82 | nfs_statfs, |
83 | nfs_sync, | |
2cd82738 | 84 | nfs_vget, |
a2907882 KM |
85 | nfs_fhtovp, |
86 | nfs_vptofh, | |
363ac00e | 87 | nfs_init, |
4acac3d6 | 88 | nfs_sysctl |
a2907882 KM |
89 | }; |
90 | ||
2c5b44a2 KM |
91 | /* |
92 | * This structure must be filled in by a primary bootstrap or bootstrap | |
93 | * server for a diskless/dataless machine. It is initialized below just | |
94 | * to ensure that it is allocated to initialized data (.data not .bss). | |
95 | */ | |
96 | struct nfs_diskless nfs_diskless = { 0 }; | |
4acac3d6 | 97 | int nfs_diskless_valid = 0; |
2c5b44a2 | 98 | |
c465e0e7 CT |
99 | void nfs_disconnect __P((struct nfsmount *)); |
100 | void nfsargs_ntoh __P((struct nfs_args *)); | |
4acac3d6 KM |
101 | int nfs_fsinfo __P((struct nfsmount *, struct vnode *, struct ucred *, |
102 | struct proc *)); | |
5f4280a2 | 103 | static int nfs_mountdiskless __P((char *, char *, int, struct sockaddr_in *, |
03e9280a | 104 | struct nfs_args *, struct proc *, struct vnode **, struct mount **)); |
f0f1cbaa | 105 | |
f0f1cbaa KM |
106 | /* |
107 | * nfs statfs call | |
108 | */ | |
c465e0e7 | 109 | int |
4ebfcce2 | 110 | nfs_statfs(mp, sbp, p) |
f0f1cbaa KM |
111 | struct mount *mp; |
112 | register struct statfs *sbp; | |
4ebfcce2 | 113 | struct proc *p; |
f0f1cbaa KM |
114 | { |
115 | register struct vnode *vp; | |
4acac3d6 | 116 | register struct nfs_statfs *sfp; |
f0f1cbaa | 117 | register caddr_t cp; |
4acac3d6 KM |
118 | register u_long *tl; |
119 | register long t1, t2; | |
f0f1cbaa | 120 | caddr_t bpos, dpos, cp2; |
4acac3d6 KM |
121 | struct nfsmount *nmp = VFSTONFS(mp); |
122 | int error = 0, v3 = (nmp->nm_flag & NFSMNT_NFSV3), retattr; | |
f0f1cbaa | 123 | struct mbuf *mreq, *mrep, *md, *mb, *mb2; |
f0f1cbaa KM |
124 | struct ucred *cred; |
125 | struct nfsnode *np; | |
4acac3d6 | 126 | u_quad_t tquad; |
f0f1cbaa | 127 | |
4acac3d6 KM |
128 | #ifndef nolint |
129 | sfp = (struct nfs_statfs *)0; | |
130 | #endif | |
131 | error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np); | |
132 | if (error) | |
f0f1cbaa KM |
133 | return (error); |
134 | vp = NFSTOV(np); | |
f0f1cbaa KM |
135 | cred = crget(); |
136 | cred->cr_ngroups = 1; | |
4acac3d6 KM |
137 | if (v3 && (nmp->nm_flag & NFSMNT_GOTFSINFO) == 0) |
138 | (void)nfs_fsinfo(nmp, vp, cred, p); | |
139 | nfsstats.rpccnt[NFSPROC_FSSTAT]++; | |
140 | nfsm_reqhead(vp, NFSPROC_FSSTAT, NFSX_FH(v3)); | |
141 | nfsm_fhtom(vp, v3); | |
142 | nfsm_request(vp, NFSPROC_FSSTAT, p, cred); | |
143 | if (v3) | |
144 | nfsm_postop_attr(vp, retattr); | |
145 | if (!error) | |
146 | nfsm_dissect(sfp, struct nfs_statfs *, NFSX_STATFS(v3)); | |
147 | sbp->f_iosize = min(nmp->nm_rsize, nmp->nm_wsize); | |
148 | if (v3) { | |
149 | sbp->f_bsize = NFS_FABLKSIZE; | |
150 | fxdr_hyper(&sfp->sf_tbytes, &tquad); | |
151 | sbp->f_blocks = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE)); | |
152 | fxdr_hyper(&sfp->sf_fbytes, &tquad); | |
153 | sbp->f_bfree = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE)); | |
154 | fxdr_hyper(&sfp->sf_abytes, &tquad); | |
155 | sbp->f_bavail = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE)); | |
156 | sbp->f_files = (fxdr_unsigned(long, sfp->sf_tfiles.nfsuquad[1]) | |
157 | & 0x7fffffff); | |
158 | sbp->f_ffree = (fxdr_unsigned(long, sfp->sf_ffiles.nfsuquad[1]) | |
159 | & 0x7fffffff); | |
41f343df | 160 | } else { |
4acac3d6 KM |
161 | sbp->f_bsize = fxdr_unsigned(long, sfp->sf_bsize); |
162 | sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks); | |
163 | sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree); | |
164 | sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail); | |
41f343df KM |
165 | sbp->f_files = 0; |
166 | sbp->f_ffree = 0; | |
167 | } | |
f0f1cbaa KM |
168 | if (sbp != &mp->mnt_stat) { |
169 | bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); | |
170 | bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); | |
171 | } | |
172 | nfsm_reqdone; | |
2c5b44a2 | 173 | vrele(vp); |
f0f1cbaa KM |
174 | crfree(cred); |
175 | return (error); | |
176 | } | |
a2907882 | 177 | |
4acac3d6 KM |
178 | /* |
179 | * nfs version 3 fsinfo rpc call | |
180 | */ | |
181 | int | |
182 | nfs_fsinfo(nmp, vp, cred, p) | |
183 | register struct nfsmount *nmp; | |
184 | register struct vnode *vp; | |
185 | struct ucred *cred; | |
186 | struct proc *p; | |
187 | { | |
188 | register struct nfsv3_fsinfo *fsp; | |
189 | register caddr_t cp; | |
190 | register long t1, t2; | |
191 | register u_long *tl, pref, max; | |
192 | caddr_t bpos, dpos, cp2; | |
193 | int error = 0, retattr; | |
194 | struct mbuf *mreq, *mrep, *md, *mb, *mb2; | |
195 | ||
196 | nfsstats.rpccnt[NFSPROC_FSINFO]++; | |
197 | nfsm_reqhead(vp, NFSPROC_FSINFO, NFSX_FH(1)); | |
198 | nfsm_fhtom(vp, 1); | |
199 | nfsm_request(vp, NFSPROC_FSINFO, p, cred); | |
200 | nfsm_postop_attr(vp, retattr); | |
201 | if (!error) { | |
202 | nfsm_dissect(fsp, struct nfsv3_fsinfo *, NFSX_V3FSINFO); | |
203 | pref = fxdr_unsigned(u_long, fsp->fs_wtpref); | |
204 | if (pref < nmp->nm_wsize) | |
205 | nmp->nm_wsize = (pref + NFS_FABLKSIZE - 1) & | |
206 | ~(NFS_FABLKSIZE - 1); | |
207 | max = fxdr_unsigned(u_long, fsp->fs_wtmax); | |
208 | if (max < nmp->nm_wsize) { | |
209 | nmp->nm_wsize = max & ~(NFS_FABLKSIZE - 1); | |
210 | if (nmp->nm_wsize == 0) | |
211 | nmp->nm_wsize = max; | |
212 | } | |
213 | pref = fxdr_unsigned(u_long, fsp->fs_rtpref); | |
214 | if (pref < nmp->nm_rsize) | |
215 | nmp->nm_rsize = (pref + NFS_FABLKSIZE - 1) & | |
216 | ~(NFS_FABLKSIZE - 1); | |
217 | max = fxdr_unsigned(u_long, fsp->fs_rtmax); | |
218 | if (max < nmp->nm_rsize) { | |
219 | nmp->nm_rsize = max & ~(NFS_FABLKSIZE - 1); | |
220 | if (nmp->nm_rsize == 0) | |
221 | nmp->nm_rsize = max; | |
222 | } | |
223 | pref = fxdr_unsigned(u_long, fsp->fs_dtpref); | |
224 | if (pref < nmp->nm_readdirsize) | |
225 | nmp->nm_readdirsize = (pref + NFS_DIRBLKSIZ - 1) & | |
226 | ~(NFS_DIRBLKSIZ - 1); | |
227 | if (max < nmp->nm_readdirsize) { | |
228 | nmp->nm_readdirsize = max & ~(NFS_DIRBLKSIZ - 1); | |
229 | if (nmp->nm_readdirsize == 0) | |
230 | nmp->nm_readdirsize = max; | |
231 | } | |
232 | nmp->nm_flag |= NFSMNT_GOTFSINFO; | |
233 | } | |
234 | nfsm_reqdone; | |
235 | return (error); | |
236 | } | |
237 | ||
a2907882 | 238 | /* |
1c89915d KM |
239 | * Mount a remote root fs via. nfs. This depends on the info in the |
240 | * nfs_diskless structure that has been filled in properly by some primary | |
241 | * bootstrap. | |
242 | * It goes something like this: | |
243 | * - do enough of "ifconfig" by calling ifioctl() so that the system | |
244 | * can talk to the server | |
245 | * - If nfs_diskless.mygateway is filled in, use that address as | |
246 | * a default gateway. | |
1c89915d | 247 | * - hand craft the swap nfs vnode hanging off a fake mount point |
2c5b44a2 | 248 | * if swdevt[0].sw_dev == NODEV |
1c89915d | 249 | * - build the rootfs mount point and call mountnfs() to do the rest. |
a2907882 | 250 | */ |
c465e0e7 | 251 | int |
a2907882 KM |
252 | nfs_mountroot() |
253 | { | |
03e9280a | 254 | struct mount *mp, *swap_mp; |
5f4280a2 | 255 | struct nfs_diskless *nd = &nfs_diskless; |
1c89915d KM |
256 | struct socket *so; |
257 | struct vnode *vp; | |
c465e0e7 | 258 | struct proc *p = curproc; /* XXX */ |
67e8af50 | 259 | int error, i; |
4acac3d6 KM |
260 | u_long l; |
261 | char buf[128]; | |
1c89915d | 262 | |
c465e0e7 CT |
263 | /* |
264 | * XXX time must be non-zero when we init the interface or else | |
265 | * the arp code will wedge... | |
266 | */ | |
267 | if (time.tv_sec == 0) | |
268 | time.tv_sec = 1; | |
269 | ||
4acac3d6 KM |
270 | /* |
271 | * XXX splnet, so networks will receive... | |
272 | */ | |
273 | splnet(); | |
274 | ||
c465e0e7 CT |
275 | #ifdef notyet |
276 | /* Set up swap credentials. */ | |
c33e9e8b KM |
277 | proc0.p_ucred->cr_uid = ntohl(nd->swap_ucred.cr_uid); |
278 | proc0.p_ucred->cr_gid = ntohl(nd->swap_ucred.cr_gid); | |
279 | if ((proc0.p_ucred->cr_ngroups = ntohs(nd->swap_ucred.cr_ngroups)) > | |
280 | NGROUPS) | |
281 | proc0.p_ucred->cr_ngroups = NGROUPS; | |
282 | for (i = 0; i < proc0.p_ucred->cr_ngroups; i++) | |
283 | proc0.p_ucred->cr_groups[i] = ntohl(nd->swap_ucred.cr_groups[i]); | |
c465e0e7 CT |
284 | #endif |
285 | ||
1c89915d | 286 | /* |
2c5b44a2 | 287 | * Do enough of ifconfig(8) so that the critical net interface can |
1c89915d KM |
288 | * talk to the server. |
289 | */ | |
4acac3d6 | 290 | error = socreate(nd->myif.ifra_addr.sa_family, &so, SOCK_DGRAM, 0); |
5f4280a2 KM |
291 | if (error) { |
292 | printf("nfs_mountroot: socreate(%04x): %d", | |
4acac3d6 | 293 | nd->myif.ifra_addr.sa_family, error); |
5f4280a2 KM |
294 | return (error); |
295 | } | |
4acac3d6 KM |
296 | |
297 | /* | |
298 | * We might not have been told the right interface, so we pass | |
299 | * over the first ten interfaces of the same kind, until we get | |
300 | * one of them configured. | |
301 | */ | |
302 | ||
303 | for (i = strlen(nd->myif.ifra_name) - 1; | |
304 | nd->myif.ifra_name[i] >= '0' && | |
305 | nd->myif.ifra_name[i] <= '9'; | |
306 | nd->myif.ifra_name[i] ++) { | |
307 | error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, p); | |
308 | if(!error) | |
309 | break; | |
310 | } | |
5f4280a2 KM |
311 | if (error) { |
312 | printf("nfs_mountroot: SIOCAIFADDR: %d", error); | |
313 | return (error); | |
314 | } | |
1c89915d KM |
315 | soclose(so); |
316 | ||
317 | /* | |
318 | * If the gateway field is filled in, set it as the default route. | |
319 | */ | |
c465e0e7 | 320 | if (nd->mygateway.sin_len != 0) { |
968f7e55 | 321 | struct sockaddr_in mask, sin; |
2c5b44a2 | 322 | |
968f7e55 KS |
323 | bzero((caddr_t)&mask, sizeof(mask)); |
324 | sin = mask; | |
2c5b44a2 | 325 | sin.sin_family = AF_INET; |
968f7e55 | 326 | sin.sin_len = sizeof(sin); |
4acac3d6 | 327 | error = rtrequest(RTM_ADD, (struct sockaddr *)&sin, |
c465e0e7 | 328 | (struct sockaddr *)&nd->mygateway, |
968f7e55 | 329 | (struct sockaddr *)&mask, |
4acac3d6 | 330 | RTF_UP | RTF_GATEWAY, (struct rtentry **)0); |
5f4280a2 KM |
331 | if (error) { |
332 | printf("nfs_mountroot: RTM_ADD: %d", error); | |
333 | return (error); | |
334 | } | |
1c89915d | 335 | } |
1c89915d | 336 | |
03e9280a | 337 | swap_mp = NULL; |
4acac3d6 KM |
338 | if (nd->swap_nblks) { |
339 | /* | |
340 | * Create a fake mount point just for the swap vnode so that the | |
341 | * swap file can be on a different server from the rootfs. | |
342 | */ | |
343 | nd->swap_args.fh = nd->swap_fh; | |
344 | /* | |
345 | * If using nfsv3_diskless, replace NFSX_V2FH with | |
346 | * nd->swap_fhsize. | |
347 | */ | |
348 | nd->swap_args.fhsize = NFSX_V2FH; | |
349 | l = ntohl(nd->swap_saddr.sin_addr.s_addr); | |
350 | sprintf(buf,"%ld.%ld.%ld.%ld:%s", | |
351 | (l >> 24) & 0xff, (l >> 16) & 0xff, | |
352 | (l >> 8) & 0xff, (l >> 0) & 0xff,nd->swap_hostnam); | |
353 | printf("NFS SWAP: %s\n",buf); | |
5f4280a2 | 354 | if (error = nfs_mountdiskless(buf, "/swap", 0, |
03e9280a | 355 | &nd->swap_saddr, &nd->swap_args, p, &vp, &swap_mp)) |
5f4280a2 | 356 | return (error); |
03e9280a | 357 | vfs_unbusy(swap_mp, p); |
4acac3d6 KM |
358 | |
359 | for (i=0;swdevt[i].sw_dev != NODEV;i++) ; | |
360 | ||
1c89915d | 361 | /* |
1c89915d KM |
362 | * Since the swap file is not the root dir of a file system, |
363 | * hack it to a regular file. | |
364 | */ | |
1c89915d KM |
365 | vp->v_type = VREG; |
366 | vp->v_flag = 0; | |
367 | swapdev_vp = vp; | |
368 | VREF(vp); | |
4acac3d6 KM |
369 | swdevt[i].sw_vp = vp; |
370 | swdevt[i].sw_nblks = nd->swap_nblks*2; | |
371 | ||
372 | if (!swdevt[i].sw_nblks) { | |
373 | swdevt[i].sw_nblks = 2048; | |
374 | printf("defaulting to %d kbyte.\n", | |
375 | swdevt[i].sw_nblks/2); | |
376 | } else | |
377 | printf("using %d kbyte.\n",swdevt[i].sw_nblks/2); | |
378 | } | |
1c89915d KM |
379 | |
380 | /* | |
381 | * Create the rootfs mount point. | |
382 | */ | |
4acac3d6 KM |
383 | nd->root_args.fh = nd->root_fh; |
384 | /* | |
385 | * If using nfsv3_diskless, replace NFSX_V2FH with nd->root_fhsize. | |
386 | */ | |
387 | nd->root_args.fhsize = NFSX_V2FH; | |
388 | l = ntohl(nd->swap_saddr.sin_addr.s_addr); | |
389 | sprintf(buf,"%ld.%ld.%ld.%ld:%s", | |
390 | (l >> 24) & 0xff, (l >> 16) & 0xff, | |
391 | (l >> 8) & 0xff, (l >> 0) & 0xff,nd->root_hostnam); | |
392 | printf("NFS ROOT: %s\n",buf); | |
5f4280a2 | 393 | if (error = nfs_mountdiskless(buf, "/", MNT_RDONLY, |
03e9280a KM |
394 | &nd->root_saddr, &nd->root_args, p, &vp, &mp)) { |
395 | if (swap_mp) { | |
396 | mp->mnt_vfc->vfc_refcount--; | |
397 | free(swap_mp, M_MOUNT); | |
398 | } | |
5f4280a2 KM |
399 | return (error); |
400 | } | |
03e9280a KM |
401 | |
402 | simple_lock(&mountlist_slock); | |
3df1ed0c | 403 | CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); |
03e9280a | 404 | simple_unlock(&mountlist_slock); |
1c89915d | 405 | rootvp = vp; |
03e9280a | 406 | vfs_unbusy(mp, p); |
26e1e7cf KM |
407 | |
408 | /* | |
409 | * This is not really an nfs issue, but it is much easier to | |
410 | * set hostname here and then let the "/etc/rc.xxx" files | |
411 | * mount the right /var based upon its preset value. | |
412 | */ | |
c465e0e7 | 413 | bcopy(nd->my_hostnam, hostname, MAXHOSTNAMELEN); |
26e1e7cf KM |
414 | hostname[MAXHOSTNAMELEN - 1] = '\0'; |
415 | for (i = 0; i < MAXHOSTNAMELEN; i++) | |
416 | if (hostname[i] == '\0') | |
417 | break; | |
418 | hostnamelen = i; | |
c33e9e8b | 419 | inittodr(ntohl(nd->root_time)); |
1c89915d | 420 | return (0); |
a2907882 KM |
421 | } |
422 | ||
c465e0e7 CT |
423 | /* |
424 | * Internal version of mount system call for diskless setup. | |
425 | */ | |
5f4280a2 | 426 | static int |
03e9280a | 427 | nfs_mountdiskless(path, which, mountflag, sin, args, p, vpp, mpp) |
c465e0e7 CT |
428 | char *path; |
429 | char *which; | |
430 | int mountflag; | |
431 | struct sockaddr_in *sin; | |
432 | struct nfs_args *args; | |
03e9280a | 433 | struct proc *p; |
5f4280a2 KM |
434 | struct vnode **vpp; |
435 | struct mount **mpp; | |
c465e0e7 | 436 | { |
5f4280a2 KM |
437 | struct mount *mp; |
438 | struct mbuf *m; | |
439 | int error; | |
c465e0e7 | 440 | |
5f4280a2 KM |
441 | if (error = vfs_rootmountalloc("nfs", path, &mp)) { |
442 | printf("nfs_mountroot: NFS not configured"); | |
443 | return (error); | |
444 | } | |
c465e0e7 | 445 | mp->mnt_flag = mountflag; |
03e9280a | 446 | MGET(m, MT_SONAME, M_WAITOK); |
c465e0e7 CT |
447 | bcopy((caddr_t)sin, mtod(m, caddr_t), sin->sin_len); |
448 | m->m_len = sin->sin_len; | |
5f4280a2 KM |
449 | if (error = mountnfs(args, mp, m, which, path, vpp)) { |
450 | printf("nfs_mountroot: mount %s on %s: %d", path, which, error); | |
03e9280a KM |
451 | mp->mnt_vfc->vfc_refcount--; |
452 | vfs_unbusy(mp, p); | |
453 | free(mp, M_MOUNT); | |
5f4280a2 KM |
454 | return (error); |
455 | } | |
456 | (void) copystr(which, mp->mnt_stat.f_mntonname, MNAMELEN - 1, 0); | |
457 | *mpp = mp; | |
458 | return (0); | |
c465e0e7 CT |
459 | } |
460 | ||
a2907882 KM |
461 | /* |
462 | * VFS Operations. | |
463 | * | |
464 | * mount system call | |
465 | * It seems a bit dumb to copyinstr() the host and path here and then | |
466 | * bcopy() them in mountnfs(), but I wanted to detect errors before | |
467 | * doing the sockargs() call because sockargs() allocates an mbuf and | |
468 | * an error after that means that I have to release the mbuf. | |
469 | */ | |
0bd503ad | 470 | /* ARGSUSED */ |
c465e0e7 | 471 | int |
4ebfcce2 | 472 | nfs_mount(mp, path, data, ndp, p) |
a2907882 KM |
473 | struct mount *mp; |
474 | char *path; | |
475 | caddr_t data; | |
476 | struct nameidata *ndp; | |
4ebfcce2 | 477 | struct proc *p; |
a2907882 KM |
478 | { |
479 | int error; | |
480 | struct nfs_args args; | |
f0f1cbaa | 481 | struct mbuf *nam; |
1c89915d | 482 | struct vnode *vp; |
a2907882 | 483 | char pth[MNAMELEN], hst[MNAMELEN]; |
4f76a9f0 | 484 | u_int len; |
4acac3d6 | 485 | u_char nfh[NFSX_V3FHMAX]; |
a2907882 | 486 | |
4acac3d6 KM |
487 | error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args)); |
488 | if (error) | |
a2907882 | 489 | return (error); |
698b1f28 KM |
490 | if (args.version != NFS_ARGSVERSION) |
491 | return (EPROGMISMATCH); | |
4acac3d6 KM |
492 | error = copyin((caddr_t)args.fh, (caddr_t)nfh, args.fhsize); |
493 | if (error) | |
a2907882 | 494 | return (error); |
4acac3d6 KM |
495 | error = copyinstr(path, pth, MNAMELEN-1, &len); |
496 | if (error) | |
a2907882 | 497 | return (error); |
4f76a9f0 | 498 | bzero(&pth[len], MNAMELEN - len); |
4acac3d6 KM |
499 | error = copyinstr(args.hostname, hst, MNAMELEN-1, &len); |
500 | if (error) | |
a2907882 | 501 | return (error); |
4f76a9f0 | 502 | bzero(&hst[len], MNAMELEN - len); |
a2907882 | 503 | /* sockargs() call must be after above copyin() calls */ |
4acac3d6 KM |
504 | error = sockargs(&nam, (caddr_t)args.addr, args.addrlen, MT_SONAME); |
505 | if (error) | |
a2907882 | 506 | return (error); |
4acac3d6 | 507 | args.fh = nfh; |
1c89915d | 508 | error = mountnfs(&args, mp, nam, pth, hst, &vp); |
a2907882 KM |
509 | return (error); |
510 | } | |
511 | ||
512 | /* | |
513 | * Common code for mount and mountroot | |
514 | */ | |
c465e0e7 | 515 | int |
1c89915d | 516 | mountnfs(argp, mp, nam, pth, hst, vpp) |
a2907882 KM |
517 | register struct nfs_args *argp; |
518 | register struct mount *mp; | |
f0f1cbaa | 519 | struct mbuf *nam; |
a2907882 | 520 | char *pth, *hst; |
1c89915d | 521 | struct vnode **vpp; |
a2907882 KM |
522 | { |
523 | register struct nfsmount *nmp; | |
9fa46e39 | 524 | struct nfsnode *np; |
4acac3d6 | 525 | int error, maxio; |
a2907882 | 526 | |
c465e0e7 CT |
527 | if (mp->mnt_flag & MNT_UPDATE) { |
528 | nmp = VFSTONFS(mp); | |
529 | /* update paths, file handles, etc, here XXX */ | |
530 | m_freem(nam); | |
531 | return (0); | |
532 | } else { | |
533 | MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount), | |
534 | M_NFSMNT, M_WAITOK); | |
535 | bzero((caddr_t)nmp, sizeof (struct nfsmount)); | |
4acac3d6 | 536 | TAILQ_INIT(&nmp->nm_uidlruhead); |
c465e0e7 CT |
537 | mp->mnt_data = (qaddr_t)nmp; |
538 | } | |
4acac3d6 | 539 | vfs_getnewfsid(mp); |
a2907882 KM |
540 | nmp->nm_mountp = mp; |
541 | nmp->nm_flag = argp->flags; | |
11488b46 | 542 | if (nmp->nm_flag & NFSMNT_NQNFS) |
460cf449 KM |
543 | /* |
544 | * We have to set mnt_maxsymlink to a non-zero value so | |
545 | * that COMPAT_43 routines will know that we are setting | |
546 | * the d_type field in directories (and can zero it for | |
547 | * unsuspecting binaries). | |
548 | */ | |
549 | mp->mnt_maxsymlinklen = 1; | |
2c5b44a2 | 550 | nmp->nm_timeo = NFS_TIMEO; |
98fb8dd3 KM |
551 | nmp->nm_retry = NFS_RETRANS; |
552 | nmp->nm_wsize = NFS_WSIZE; | |
553 | nmp->nm_rsize = NFS_RSIZE; | |
4acac3d6 | 554 | nmp->nm_readdirsize = NFS_READDIRSIZE; |
2c5b44a2 KM |
555 | nmp->nm_numgrps = NFS_MAXGRPS; |
556 | nmp->nm_readahead = NFS_DEFRAHEAD; | |
557 | nmp->nm_leaseterm = NQ_DEFLEASE; | |
558 | nmp->nm_deadthresh = NQ_DEADTHRESH; | |
0bb2c2fc | 559 | CIRCLEQ_INIT(&nmp->nm_timerhead); |
2c5b44a2 | 560 | nmp->nm_inprog = NULLVP; |
4acac3d6 KM |
561 | nmp->nm_fhsize = argp->fhsize; |
562 | bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize); | |
54fb9dc2 KM |
563 | bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN); |
564 | bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN); | |
f0f1cbaa | 565 | nmp->nm_nam = nam; |
98fb8dd3 KM |
566 | |
567 | if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) { | |
2c5b44a2 KM |
568 | nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10; |
569 | if (nmp->nm_timeo < NFS_MINTIMEO) | |
570 | nmp->nm_timeo = NFS_MINTIMEO; | |
571 | else if (nmp->nm_timeo > NFS_MAXTIMEO) | |
572 | nmp->nm_timeo = NFS_MAXTIMEO; | |
98fb8dd3 KM |
573 | } |
574 | ||
d8792713 | 575 | if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) { |
98fb8dd3 KM |
576 | nmp->nm_retry = argp->retrans; |
577 | if (nmp->nm_retry > NFS_MAXREXMIT) | |
578 | nmp->nm_retry = NFS_MAXREXMIT; | |
579 | } | |
580 | ||
4acac3d6 KM |
581 | if (argp->flags & NFSMNT_NFSV3) { |
582 | if (argp->sotype == SOCK_DGRAM) | |
583 | maxio = NFS_MAXDGRAMDATA; | |
584 | else | |
585 | maxio = NFS_MAXDATA; | |
586 | } else | |
587 | maxio = NFS_V2MAXDATA; | |
588 | ||
98fb8dd3 | 589 | if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) { |
a2907882 | 590 | nmp->nm_wsize = argp->wsize; |
98fb8dd3 | 591 | /* Round down to multiple of blocksize */ |
4acac3d6 | 592 | nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1); |
98fb8dd3 | 593 | if (nmp->nm_wsize <= 0) |
4acac3d6 | 594 | nmp->nm_wsize = NFS_FABLKSIZE; |
98fb8dd3 | 595 | } |
4acac3d6 KM |
596 | if (nmp->nm_wsize > maxio) |
597 | nmp->nm_wsize = maxio; | |
d8792713 KM |
598 | if (nmp->nm_wsize > MAXBSIZE) |
599 | nmp->nm_wsize = MAXBSIZE; | |
98fb8dd3 KM |
600 | |
601 | if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) { | |
a2907882 | 602 | nmp->nm_rsize = argp->rsize; |
98fb8dd3 | 603 | /* Round down to multiple of blocksize */ |
4acac3d6 | 604 | nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1); |
98fb8dd3 | 605 | if (nmp->nm_rsize <= 0) |
4acac3d6 | 606 | nmp->nm_rsize = NFS_FABLKSIZE; |
98fb8dd3 | 607 | } |
4acac3d6 KM |
608 | if (nmp->nm_rsize > maxio) |
609 | nmp->nm_rsize = maxio; | |
d8792713 KM |
610 | if (nmp->nm_rsize > MAXBSIZE) |
611 | nmp->nm_rsize = MAXBSIZE; | |
4acac3d6 KM |
612 | |
613 | if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) { | |
614 | nmp->nm_readdirsize = argp->readdirsize; | |
615 | /* Round down to multiple of blocksize */ | |
616 | nmp->nm_readdirsize &= ~(NFS_DIRBLKSIZ - 1); | |
617 | if (nmp->nm_readdirsize < NFS_DIRBLKSIZ) | |
618 | nmp->nm_readdirsize = NFS_DIRBLKSIZ; | |
619 | } | |
620 | if (nmp->nm_readdirsize > maxio) | |
621 | nmp->nm_readdirsize = maxio; | |
622 | ||
2c5b44a2 KM |
623 | if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 && |
624 | argp->maxgrouplist <= NFS_MAXGRPS) | |
625 | nmp->nm_numgrps = argp->maxgrouplist; | |
626 | if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0 && | |
627 | argp->readahead <= NFS_MAXRAHEAD) | |
628 | nmp->nm_readahead = argp->readahead; | |
629 | if ((argp->flags & NFSMNT_LEASETERM) && argp->leaseterm >= 2 && | |
630 | argp->leaseterm <= NQ_MAXLEASE) | |
631 | nmp->nm_leaseterm = argp->leaseterm; | |
632 | if ((argp->flags & NFSMNT_DEADTHRESH) && argp->deadthresh >= 1 && | |
633 | argp->deadthresh <= NQ_NEVERDEAD) | |
634 | nmp->nm_deadthresh = argp->deadthresh; | |
98fb8dd3 | 635 | /* Set up the sockets and per-host congestion */ |
f0f1cbaa KM |
636 | nmp->nm_sotype = argp->sotype; |
637 | nmp->nm_soproto = argp->proto; | |
98fb8dd3 | 638 | |
2c5b44a2 KM |
639 | /* |
640 | * For Connection based sockets (TCP,...) defer the connect until | |
641 | * the first request, in case the server is not responding. | |
642 | */ | |
643 | if (nmp->nm_sotype == SOCK_DGRAM && | |
644 | (error = nfs_connect(nmp, (struct nfsreq *)0))) | |
efaa4294 | 645 | goto bad; |
2c5b44a2 KM |
646 | |
647 | /* | |
648 | * This is silly, but it has to be set so that vinifod() works. | |
649 | * We do not want to do an nfs_statfs() here since we can get | |
650 | * stuck on a dead server and we are holding a lock on the mount | |
651 | * point. | |
652 | */ | |
653 | mp->mnt_stat.f_iosize = NFS_MAXDGRAMDATA; | |
9fa46e39 KM |
654 | /* |
655 | * A reference count is needed on the nfsnode representing the | |
656 | * remote root. If this object is not persistent, then backward | |
657 | * traversals of the mount point (i.e. "..") will not work if | |
658 | * the nfsnode gets flushed out of the cache. Ufs does not have | |
659 | * this problem, because one can identify root inodes by their | |
660 | * number == ROOTINO (2). | |
661 | */ | |
4acac3d6 KM |
662 | error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np); |
663 | if (error) | |
9fa46e39 | 664 | goto bad; |
1c89915d | 665 | *vpp = NFSTOV(np); |
efaa4294 | 666 | |
f0f1cbaa | 667 | return (0); |
a2907882 | 668 | bad: |
98fb8dd3 | 669 | nfs_disconnect(nmp); |
2c5b44a2 | 670 | free((caddr_t)nmp, M_NFSMNT); |
f0f1cbaa | 671 | m_freem(nam); |
a2907882 KM |
672 | return (error); |
673 | } | |
674 | ||
675 | /* | |
676 | * unmount system call | |
677 | */ | |
c465e0e7 | 678 | int |
4ebfcce2 | 679 | nfs_unmount(mp, mntflags, p) |
a2907882 | 680 | struct mount *mp; |
56cf2c46 | 681 | int mntflags; |
4ebfcce2 | 682 | struct proc *p; |
a2907882 KM |
683 | { |
684 | register struct nfsmount *nmp; | |
9fa46e39 | 685 | struct nfsnode *np; |
98fb8dd3 | 686 | struct vnode *vp; |
6556ebbd | 687 | int error, flags = 0; |
a2907882 | 688 | |
fe5239dc | 689 | if (mntflags & MNT_FORCE) |
56cf2c46 | 690 | flags |= FORCECLOSE; |
54fb9dc2 | 691 | nmp = VFSTONFS(mp); |
a2907882 KM |
692 | /* |
693 | * Goes something like this.. | |
98fb8dd3 KM |
694 | * - Check for activity on the root vnode (other than ourselves). |
695 | * - Call vflush() to clear out vnodes for this file system, | |
696 | * except for the root vnode. | |
697 | * - Decrement reference on the vnode representing remote root. | |
a2907882 KM |
698 | * - Close the socket |
699 | * - Free up the data structures | |
700 | */ | |
9fa46e39 KM |
701 | /* |
702 | * We need to decrement the ref. count on the nfsnode representing | |
703 | * the remote root. See comment in mountnfs(). The VFS unmount() | |
704 | * has done vput on this vnode, otherwise we would get deadlock! | |
705 | */ | |
4acac3d6 KM |
706 | error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np); |
707 | if (error) | |
9fa46e39 | 708 | return(error); |
98fb8dd3 KM |
709 | vp = NFSTOV(np); |
710 | if (vp->v_usecount > 2) { | |
711 | vput(vp); | |
712 | return (EBUSY); | |
713 | } | |
2c5b44a2 KM |
714 | |
715 | /* | |
716 | * Must handshake with nqnfs_clientd() if it is active. | |
717 | */ | |
718 | nmp->nm_flag |= NFSMNT_DISMINPROG; | |
719 | while (nmp->nm_inprog != NULLVP) | |
720 | (void) tsleep((caddr_t)&lbolt, PSOCK, "nfsdism", 0); | |
4acac3d6 KM |
721 | error = vflush(mp, vp, flags); |
722 | if (error) { | |
98fb8dd3 | 723 | vput(vp); |
2c5b44a2 | 724 | nmp->nm_flag &= ~NFSMNT_DISMINPROG; |
a2907882 | 725 | return (error); |
98fb8dd3 | 726 | } |
2c5b44a2 KM |
727 | |
728 | /* | |
729 | * We are now committed to the unmount. | |
730 | * For NQNFS, let the server daemon free the nfsmount structure. | |
731 | */ | |
732 | if (nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB)) | |
733 | nmp->nm_flag |= NFSMNT_DISMNT; | |
734 | ||
a2907882 | 735 | /* |
2c5b44a2 | 736 | * There are two reference counts to get rid of here. |
a2907882 | 737 | */ |
98fb8dd3 | 738 | vrele(vp); |
2c5b44a2 | 739 | vrele(vp); |
8473f8d7 | 740 | vgone(vp); |
98fb8dd3 | 741 | nfs_disconnect(nmp); |
f0f1cbaa | 742 | m_freem(nmp->nm_nam); |
2c5b44a2 KM |
743 | |
744 | if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_KERB)) == 0) | |
745 | free((caddr_t)nmp, M_NFSMNT); | |
a2907882 KM |
746 | return (0); |
747 | } | |
748 | ||
749 | /* | |
750 | * Return root of a filesystem | |
751 | */ | |
c465e0e7 | 752 | int |
a2907882 KM |
753 | nfs_root(mp, vpp) |
754 | struct mount *mp; | |
755 | struct vnode **vpp; | |
756 | { | |
757 | register struct vnode *vp; | |
758 | struct nfsmount *nmp; | |
759 | struct nfsnode *np; | |
760 | int error; | |
761 | ||
54fb9dc2 | 762 | nmp = VFSTONFS(mp); |
4acac3d6 KM |
763 | error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np); |
764 | if (error) | |
a2907882 KM |
765 | return (error); |
766 | vp = NFSTOV(np); | |
767 | vp->v_type = VDIR; | |
768 | vp->v_flag = VROOT; | |
769 | *vpp = vp; | |
770 | return (0); | |
771 | } | |
772 | ||
9238aa59 RM |
773 | extern int syncprt; |
774 | ||
a2907882 | 775 | /* |
9238aa59 | 776 | * Flush out the buffer cache |
a2907882 | 777 | */ |
0bd503ad | 778 | /* ARGSUSED */ |
c465e0e7 | 779 | int |
050651c3 | 780 | nfs_sync(mp, waitfor, cred, p) |
a2907882 KM |
781 | struct mount *mp; |
782 | int waitfor; | |
050651c3 KM |
783 | struct ucred *cred; |
784 | struct proc *p; | |
a2907882 | 785 | { |
050651c3 KM |
786 | register struct vnode *vp; |
787 | int error, allerror = 0; | |
788 | ||
9238aa59 RM |
789 | /* |
790 | * Force stale buffer cache information to be flushed. | |
791 | */ | |
050651c3 | 792 | loop: |
d26dac13 KM |
793 | for (vp = mp->mnt_vnodelist.lh_first; |
794 | vp != NULL; | |
795 | vp = vp->v_mntvnodes.le_next) { | |
050651c3 KM |
796 | /* |
797 | * If the vnode that we are about to sync is no longer | |
798 | * associated with this mount point, start over. | |
799 | */ | |
800 | if (vp->v_mount != mp) | |
801 | goto loop; | |
d26dac13 | 802 | if (VOP_ISLOCKED(vp) || vp->v_dirtyblkhd.lh_first == NULL) |
050651c3 | 803 | continue; |
d0b01123 | 804 | if (vget(vp, LK_EXCLUSIVE, p)) |
050651c3 | 805 | goto loop; |
4acac3d6 KM |
806 | error = VOP_FSYNC(vp, cred, waitfor, p); |
807 | if (error) | |
050651c3 KM |
808 | allerror = error; |
809 | vput(vp); | |
810 | } | |
811 | return (allerror); | |
a2907882 KM |
812 | } |
813 | ||
2cd82738 KM |
814 | /* |
815 | * NFS flat namespace lookup. | |
816 | * Currently unsupported. | |
817 | */ | |
818 | /* ARGSUSED */ | |
819 | int | |
820 | nfs_vget(mp, ino, vpp) | |
821 | struct mount *mp; | |
822 | ino_t ino; | |
823 | struct vnode **vpp; | |
824 | { | |
825 | ||
826 | return (EOPNOTSUPP); | |
827 | } | |
828 | ||
a2907882 KM |
829 | /* |
830 | * At this point, this should never happen | |
831 | */ | |
0bd503ad | 832 | /* ARGSUSED */ |
c465e0e7 | 833 | int |
9580c523 KM |
834 | nfs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp) |
835 | register struct mount *mp; | |
a2907882 | 836 | struct fid *fhp; |
9580c523 | 837 | struct mbuf *nam; |
a2907882 | 838 | struct vnode **vpp; |
9580c523 KM |
839 | int *exflagsp; |
840 | struct ucred **credanonp; | |
a2907882 | 841 | { |
0bd503ad | 842 | |
a2907882 KM |
843 | return (EINVAL); |
844 | } | |
845 | ||
846 | /* | |
847 | * Vnode pointer to File handle, should never happen either | |
848 | */ | |
0bd503ad | 849 | /* ARGSUSED */ |
c465e0e7 | 850 | int |
4ebfcce2 KM |
851 | nfs_vptofh(vp, fhp) |
852 | struct vnode *vp; | |
a2907882 | 853 | struct fid *fhp; |
a2907882 | 854 | { |
0bd503ad | 855 | |
a2907882 KM |
856 | return (EINVAL); |
857 | } | |
9238aa59 RM |
858 | |
859 | /* | |
860 | * Vfs start routine, a no-op. | |
861 | */ | |
0bd503ad | 862 | /* ARGSUSED */ |
c465e0e7 | 863 | int |
4ebfcce2 | 864 | nfs_start(mp, flags, p) |
9238aa59 RM |
865 | struct mount *mp; |
866 | int flags; | |
4ebfcce2 | 867 | struct proc *p; |
9238aa59 | 868 | { |
0bd503ad | 869 | |
9238aa59 RM |
870 | return (0); |
871 | } | |
56cf2c46 KM |
872 | |
873 | /* | |
874 | * Do operations associated with quotas, not supported | |
875 | */ | |
e0f6df7b | 876 | /* ARGSUSED */ |
c465e0e7 | 877 | int |
4ebfcce2 | 878 | nfs_quotactl(mp, cmd, uid, arg, p) |
56cf2c46 KM |
879 | struct mount *mp; |
880 | int cmd; | |
050651c3 | 881 | uid_t uid; |
56cf2c46 | 882 | caddr_t arg; |
4ebfcce2 | 883 | struct proc *p; |
56cf2c46 | 884 | { |
e0f6df7b | 885 | |
56cf2c46 KM |
886 | return (EOPNOTSUPP); |
887 | } | |
4acac3d6 KM |
888 | |
889 | /* | |
890 | * Do that sysctl thang... | |
891 | */ | |
892 | static int | |
893 | nfs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, | |
894 | size_t newlen, struct proc *p) | |
895 | { | |
896 | int rv; | |
897 | ||
898 | /* | |
899 | * All names at this level are terminal. | |
900 | */ | |
901 | if(namelen > 1) | |
902 | return ENOTDIR; /* overloaded */ | |
903 | ||
904 | switch(name[0]) { | |
905 | case NFS_NFSSTATS: | |
906 | if(!oldp) { | |
907 | *oldlenp = sizeof nfsstats; | |
908 | return 0; | |
909 | } | |
910 | ||
911 | if(*oldlenp < sizeof nfsstats) { | |
912 | *oldlenp = sizeof nfsstats; | |
913 | return ENOMEM; | |
914 | } | |
915 | ||
916 | rv = copyout(&nfsstats, oldp, sizeof nfsstats); | |
917 | if(rv) return rv; | |
918 | ||
919 | if(newp && newlen != sizeof nfsstats) | |
920 | return EINVAL; | |
921 | ||
922 | if(newp) { | |
923 | return copyin(newp, &nfsstats, sizeof nfsstats); | |
924 | } | |
925 | return 0; | |
926 | ||
927 | default: | |
928 | return EOPNOTSUPP; | |
929 | } | |
930 | } | |
931 |