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