This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / sys / nfs / nfs_vfsops.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1989 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
7 *
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.
23 *
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 *
78ed81a3 36 * From: @(#)nfs_vfsops.c 7.31 (Berkeley) 5/6/91
37 * $Id$
15637ed4
RG
38 */
39
40#include "param.h"
41#include "conf.h"
42#include "ioctl.h"
43#include "signal.h"
44#include "proc.h"
45#include "namei.h"
46#include "vnode.h"
47#include "mount.h"
48#include "buf.h"
49#include "mbuf.h"
50#include "socket.h"
78ed81a3 51#include "socketvar.h"
15637ed4
RG
52#include "systm.h"
53
54#include "../net/if.h"
55#include "../net/route.h"
56#include "../netinet/in.h"
57
58#include "nfsv2.h"
59#include "nfsnode.h"
60#include "nfsmount.h"
61#include "nfs.h"
62#include "xdr_subs.h"
63#include "nfsm_subs.h"
64#include "nfsdiskless.h"
65
66/*
67 * nfs vfs operations.
68 */
69struct vfsops nfs_vfsops = {
70 nfs_mount,
71 nfs_start,
72 nfs_unmount,
73 nfs_root,
74 nfs_quotactl,
75 nfs_statfs,
76 nfs_sync,
77 nfs_fhtovp,
78 nfs_vptofh,
79 nfs_init,
80};
81
82static u_char nfs_mntid;
83extern u_long nfs_procids[NFS_NPROCS];
84extern u_long nfs_prog, nfs_vers;
85struct nfs_diskless nfs_diskless;
86void nfs_disconnect();
87
88#define TRUE 1
89#define FALSE 0
90
91/*
92 * nfs statfs call
93 */
78ed81a3 94int
15637ed4
RG
95nfs_statfs(mp, sbp, p)
96 struct mount *mp;
97 register struct statfs *sbp;
98 struct proc *p;
99{
100 register struct vnode *vp;
101 register struct nfsv2_statfs *sfp;
102 register caddr_t cp;
103 register long t1;
104 caddr_t bpos, dpos, cp2;
105 u_long xid;
106 int error = 0;
107 struct mbuf *mreq, *mrep, *md, *mb, *mb2;
108 struct nfsmount *nmp;
109 struct ucred *cred;
110 struct nfsnode *np;
111
112 nmp = VFSTONFS(mp);
113 if (error = nfs_nget(mp, &nmp->nm_fh, &np))
114 return (error);
115 vp = NFSTOV(np);
116 nfsstats.rpccnt[NFSPROC_STATFS]++;
117 cred = crget();
118 cred->cr_ngroups = 1;
119 nfsm_reqhead(nfs_procids[NFSPROC_STATFS], cred, NFSX_FH);
120 nfsm_fhtom(vp);
121 nfsm_request(vp, NFSPROC_STATFS, p, 0);
122 nfsm_disect(sfp, struct nfsv2_statfs *, NFSX_STATFS);
123 sbp->f_type = MOUNT_NFS;
124 sbp->f_flags = nmp->nm_flag;
125 sbp->f_bsize = fxdr_unsigned(long, sfp->sf_tsize);
126 sbp->f_fsize = fxdr_unsigned(long, sfp->sf_bsize);
127 sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks);
128 sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree);
129 sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail);
130 sbp->f_files = 0;
131 sbp->f_ffree = 0;
132 if (sbp != &mp->mnt_stat) {
133 bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
134 bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
135 }
136 nfsm_reqdone;
137 nfs_nput(vp);
138 crfree(cred);
139 return (error);
140}
141
142/*
143 * Mount a remote root fs via. nfs. This depends on the info in the
144 * nfs_diskless structure that has been filled in properly by some primary
145 * bootstrap.
146 * It goes something like this:
147 * - do enough of "ifconfig" by calling ifioctl() so that the system
148 * can talk to the server
149 * - If nfs_diskless.mygateway is filled in, use that address as
150 * a default gateway.
151 * (This is done the 4.3 way with rtioctl() and should be changed)
152 * - hand craft the swap nfs vnode hanging off a fake mount point
153 * - build the rootfs mount point and call mountnfs() to do the rest.
154 */
78ed81a3 155int
15637ed4
RG
156nfs_mountroot()
157{
158 register struct mount *mp;
159 register struct mbuf *m;
160 struct socket *so;
161 struct vnode *vp;
78ed81a3 162/* int error;*/
15637ed4
RG
163
164 /*
165 * Do enough of ifconfig(8) so that critical net interface can
166 * talk to the server.
167 */
168 if (socreate(nfs_diskless.myif.ifra_addr.sa_family, &so, SOCK_DGRAM, 0))
169 panic("nfs ifconf");
78ed81a3 170 /* XXX - this probably will crash with a vm_fault, since
171 ifioctl expects to get a valid process pointer */
172 if (ifioctl(so, SIOCAIFADDR, (caddr_t)&nfs_diskless.myif, 0))
15637ed4
RG
173 panic("nfs ifconf2");
174 soclose(so);
175
176 /*
177 * If the gateway field is filled in, set it as the default route.
178 */
179#ifdef COMPAT_43
180 if (nfs_diskless.mygateway.sa_family == AF_INET) {
181 struct ortentry rt;
182 struct sockaddr_in *sin;
183
184 sin = (struct sockaddr_in *) &rt.rt_dst;
185 sin->sin_len = sizeof (struct sockaddr_in);
186 sin->sin_family = AF_INET;
187 sin->sin_addr.s_addr = 0; /* default */
188 bcopy((caddr_t)&nfs_diskless.mygateway, (caddr_t)&rt.rt_gateway,
189 sizeof (struct sockaddr_in));
190 rt.rt_flags = (RTF_UP | RTF_GATEWAY);
78ed81a3 191 {
192 /* copied over from rtioctl() -GW */
193 extern struct sockaddr_in icmpmask;
194 if (rtrequest(RTM_ADD, &rt.rt_dst, &rt.rt_gateway,
195 (struct sockaddr *)&icmpmask,
196 RTF_UP | RTF_GATEWAY,
197 (struct rtentry **)0))
198 panic("nfs root route");
199 }
15637ed4
RG
200 }
201#endif /* COMPAT_43 */
202
203 /*
204 * If swapping to an nfs node (indicated by swdevt[0].sw_dev == NODEV):
205 * Create a fake mount point just for the swap vnode so that the
206 * swap file can be on a different server from the rootfs.
207 */
208 if (swdevt[0].sw_dev == NODEV) {
209 mp = (struct mount *)malloc((u_long)sizeof(struct mount),
210 M_MOUNT, M_NOWAIT);
211 if (mp == NULL)
212 panic("nfs root mount");
213 mp->mnt_op = &nfs_vfsops;
214 mp->mnt_flag = 0;
215 mp->mnt_exroot = 0;
216 mp->mnt_mounth = NULLVP;
217
218 /*
219 * Set up the diskless nfs_args for the swap mount point
220 * and then call mountnfs() to mount it.
221 * Since the swap file is not the root dir of a file system,
222 * hack it to a regular file.
223 */
224 nfs_diskless.swap_args.fh = (nfsv2fh_t *)nfs_diskless.swap_fh;
225 MGET(m, MT_SONAME, M_DONTWAIT);
226 if (m == NULL)
227 panic("nfs root mbuf");
228 bcopy((caddr_t)&nfs_diskless.swap_saddr, mtod(m, caddr_t),
229 nfs_diskless.swap_saddr.sa_len);
230 m->m_len = nfs_diskless.swap_saddr.sa_len;
231 if (mountnfs(&nfs_diskless.swap_args, mp, m, "/swap",
232 nfs_diskless.swap_hostnam, &vp))
233 panic("nfs swap");
234 vp->v_type = VREG;
235 vp->v_flag = 0;
236 swapdev_vp = vp;
237 VREF(vp);
238 swdevt[0].sw_vp = vp;
239 }
240
241 /*
242 * Create the rootfs mount point.
243 */
244 mp = (struct mount *)malloc((u_long)sizeof(struct mount),
245 M_MOUNT, M_NOWAIT);
246 if (mp == NULL)
247 panic("nfs root mount2");
248 mp->mnt_op = &nfs_vfsops;
249 mp->mnt_flag = MNT_RDONLY;
250 mp->mnt_exroot = 0;
251 mp->mnt_mounth = NULLVP;
252
253 /*
254 * Set up the root fs args and call mountnfs() to do the rest.
255 */
256 nfs_diskless.root_args.fh = (nfsv2fh_t *)nfs_diskless.root_fh;
257 MGET(m, MT_SONAME, M_DONTWAIT);
258 if (m == NULL)
259 panic("nfs root mbuf2");
260 bcopy((caddr_t)&nfs_diskless.root_saddr, mtod(m, caddr_t),
261 nfs_diskless.root_saddr.sa_len);
262 m->m_len = nfs_diskless.root_saddr.sa_len;
263 if (mountnfs(&nfs_diskless.root_args, mp, m, "/",
264 nfs_diskless.root_hostnam, &vp))
265 panic("nfs root");
266 if (vfs_lock(mp))
267 panic("nfs root2");
268 rootfs = mp;
269 mp->mnt_next = mp;
270 mp->mnt_prev = mp;
271 mp->mnt_vnodecovered = NULLVP;
272 vfs_unlock(mp);
273 rootvp = vp;
78ed81a3 274#ifndef __386BSD__
15637ed4 275 inittodr((time_t)0); /* There is no time in the nfs fsstat so ?? */
78ed81a3 276#endif
15637ed4
RG
277 return (0);
278}
279
280/*
281 * VFS Operations.
282 *
283 * mount system call
284 * It seems a bit dumb to copyinstr() the host and path here and then
285 * bcopy() them in mountnfs(), but I wanted to detect errors before
286 * doing the sockargs() call because sockargs() allocates an mbuf and
287 * an error after that means that I have to release the mbuf.
288 */
289/* ARGSUSED */
78ed81a3 290int
15637ed4
RG
291nfs_mount(mp, path, data, ndp, p)
292 struct mount *mp;
293 char *path;
294 caddr_t data;
295 struct nameidata *ndp;
296 struct proc *p;
297{
298 int error;
299 struct nfs_args args;
300 struct mbuf *nam;
301 struct vnode *vp;
302 char pth[MNAMELEN], hst[MNAMELEN];
303 u_int len;
304 nfsv2fh_t nfh;
305
306 if (mp->mnt_flag & MNT_UPDATE)
307 return (0);
308 if (error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args)))
309 return (error);
310 if (error = copyin((caddr_t)args.fh, (caddr_t)&nfh, sizeof (nfsv2fh_t)))
311 return (error);
312 if (error = copyinstr(path, pth, MNAMELEN-1, &len))
313 return (error);
314 bzero(&pth[len], MNAMELEN - len);
315 if (error = copyinstr(args.hostname, hst, MNAMELEN-1, &len))
316 return (error);
317 bzero(&hst[len], MNAMELEN - len);
318 /* sockargs() call must be after above copyin() calls */
319 if (error = sockargs(&nam, (caddr_t)args.addr,
320 sizeof (struct sockaddr), MT_SONAME))
321 return (error);
322 args.fh = &nfh;
323 error = mountnfs(&args, mp, nam, pth, hst, &vp);
324 return (error);
325}
326
327/*
328 * Common code for mount and mountroot
329 */
78ed81a3 330int
15637ed4
RG
331mountnfs(argp, mp, nam, pth, hst, vpp)
332 register struct nfs_args *argp;
333 register struct mount *mp;
334 struct mbuf *nam;
335 char *pth, *hst;
336 struct vnode **vpp;
337{
338 register struct nfsmount *nmp;
339 struct proc *p = curproc; /* XXX */
340 struct nfsnode *np;
341 int error;
342 fsid_t tfsid;
343
344 MALLOC(nmp, struct nfsmount *, sizeof *nmp, M_NFSMNT, M_WAITOK);
345 bzero((caddr_t)nmp, sizeof *nmp);
346 mp->mnt_data = (qaddr_t)nmp;
347 /*
348 * Generate a unique nfs mount id. The problem is that a dev number
349 * is not unique across multiple systems. The techique is as follows:
350 * 1) Set to nblkdev,0 which will never be used otherwise
351 * 2) Generate a first guess as nblkdev,nfs_mntid where nfs_mntid is
352 * NOT 0
353 * 3) Loop searching the mount list for another one with same id
354 * If a match, increment val[0] and try again
355 * NB: I increment val[0] { a long } instead of nfs_mntid { a u_char }
356 * so that nfs is not limited to 255 mount points
357 * Incrementing the high order bits does no real harm, since it
358 * simply makes the major dev number tick up. The upper bound is
359 * set to major dev 127 to avoid any sign extention problems
360 */
361 mp->mnt_stat.f_fsid.val[0] = makedev(nblkdev, 0);
362 mp->mnt_stat.f_fsid.val[1] = MOUNT_NFS;
363 if (++nfs_mntid == 0)
364 ++nfs_mntid;
365 tfsid.val[0] = makedev(nblkdev, nfs_mntid);
366 tfsid.val[1] = MOUNT_NFS;
367 while (rootfs && getvfs(&tfsid)) {
368 tfsid.val[0]++;
369 nfs_mntid++;
370 }
371 if (major(tfsid.val[0]) > 127) {
372 error = ENOENT;
373 goto bad;
374 }
375 mp->mnt_stat.f_fsid.val[0] = tfsid.val[0];
376 nmp->nm_mountp = mp;
377 nmp->nm_flag = argp->flags;
378 nmp->nm_rto = NFS_TIMEO;
379 nmp->nm_rtt = -1;
380 nmp->nm_rttvar = nmp->nm_rto << 1;
381 nmp->nm_retry = NFS_RETRANS;
382 nmp->nm_wsize = NFS_WSIZE;
383 nmp->nm_rsize = NFS_RSIZE;
384 bcopy((caddr_t)argp->fh, (caddr_t)&nmp->nm_fh, sizeof(nfsv2fh_t));
385 bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
386 bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN);
387 nmp->nm_nam = nam;
388
389 if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
390 nmp->nm_rto = argp->timeo;
391 /* NFS timeouts are specified in 1/10 sec. */
392 nmp->nm_rto = (nmp->nm_rto * 10) / NFS_HZ;
393 if (nmp->nm_rto < NFS_MINTIMEO)
394 nmp->nm_rto = NFS_MINTIMEO;
395 else if (nmp->nm_rto > NFS_MAXTIMEO)
396 nmp->nm_rto = NFS_MAXTIMEO;
397 nmp->nm_rttvar = nmp->nm_rto << 1;
398 }
399
400 if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
401 nmp->nm_retry = argp->retrans;
402 if (nmp->nm_retry > NFS_MAXREXMIT)
403 nmp->nm_retry = NFS_MAXREXMIT;
404 }
405
406 if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
407 nmp->nm_wsize = argp->wsize;
408 /* Round down to multiple of blocksize */
409 nmp->nm_wsize &= ~0x1ff;
410 if (nmp->nm_wsize <= 0)
411 nmp->nm_wsize = 512;
412 else if (nmp->nm_wsize > NFS_MAXDATA)
413 nmp->nm_wsize = NFS_MAXDATA;
414 }
415 if (nmp->nm_wsize > MAXBSIZE)
416 nmp->nm_wsize = MAXBSIZE;
417
418 if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
419 nmp->nm_rsize = argp->rsize;
420 /* Round down to multiple of blocksize */
421 nmp->nm_rsize &= ~0x1ff;
422 if (nmp->nm_rsize <= 0)
423 nmp->nm_rsize = 512;
424 else if (nmp->nm_rsize > NFS_MAXDATA)
425 nmp->nm_rsize = NFS_MAXDATA;
426 }
427 if (nmp->nm_rsize > MAXBSIZE)
428 nmp->nm_rsize = MAXBSIZE;
429 /* Set up the sockets and per-host congestion */
430 nmp->nm_sotype = argp->sotype;
431 nmp->nm_soproto = argp->proto;
432 if (error = nfs_connect(nmp))
433 goto bad;
434
435 if (error = nfs_statfs(mp, &mp->mnt_stat, p))
436 goto bad;
437 /*
438 * A reference count is needed on the nfsnode representing the
439 * remote root. If this object is not persistent, then backward
440 * traversals of the mount point (i.e. "..") will not work if
441 * the nfsnode gets flushed out of the cache. Ufs does not have
442 * this problem, because one can identify root inodes by their
443 * number == ROOTINO (2).
444 */
445 if (error = nfs_nget(mp, &nmp->nm_fh, &np))
446 goto bad;
447 /*
448 * Unlock it, but keep the reference count.
449 */
450 nfs_unlock(NFSTOV(np));
451 *vpp = NFSTOV(np);
452
453 return (0);
454bad:
455 nfs_disconnect(nmp);
456 FREE(nmp, M_NFSMNT);
457 m_freem(nam);
458 return (error);
459}
460
461/*
462 * unmount system call
463 */
78ed81a3 464int
15637ed4
RG
465nfs_unmount(mp, mntflags, p)
466 struct mount *mp;
467 int mntflags;
468 struct proc *p;
469{
470 register struct nfsmount *nmp;
471 struct nfsnode *np;
472 struct vnode *vp;
473 int error, flags = 0;
474 extern int doforce;
475
476 if (mntflags & MNT_FORCE) {
477 if (!doforce || mp == rootfs)
478 return (EINVAL);
479 flags |= FORCECLOSE;
480 }
481 nmp = VFSTONFS(mp);
482 /*
483 * Clear out the buffer cache
484 */
485 mntflushbuf(mp, 0);
486 if (mntinvalbuf(mp))
487 return (EBUSY);
488 /*
489 * Goes something like this..
490 * - Check for activity on the root vnode (other than ourselves).
491 * - Call vflush() to clear out vnodes for this file system,
492 * except for the root vnode.
493 * - Decrement reference on the vnode representing remote root.
494 * - Close the socket
495 * - Free up the data structures
496 */
497 /*
498 * We need to decrement the ref. count on the nfsnode representing
499 * the remote root. See comment in mountnfs(). The VFS unmount()
500 * has done vput on this vnode, otherwise we would get deadlock!
501 */
502 if (error = nfs_nget(mp, &nmp->nm_fh, &np))
503 return(error);
504 vp = NFSTOV(np);
505 if (vp->v_usecount > 2) {
506 vput(vp);
507 return (EBUSY);
508 }
509 if (error = vflush(mp, vp, flags)) {
510 vput(vp);
511 return (error);
512 }
513 /*
514 * Get rid of two reference counts, and unlock it on the second.
515 */
516 vrele(vp);
517 vput(vp);
518 nfs_disconnect(nmp);
519 m_freem(nmp->nm_nam);
520 free((caddr_t)nmp, M_NFSMNT);
521 return (0);
522}
523
524/*
525 * Return root of a filesystem
526 */
78ed81a3 527int
15637ed4
RG
528nfs_root(mp, vpp)
529 struct mount *mp;
530 struct vnode **vpp;
531{
532 register struct vnode *vp;
533 struct nfsmount *nmp;
534 struct nfsnode *np;
535 int error;
536
537 nmp = VFSTONFS(mp);
538 if (error = nfs_nget(mp, &nmp->nm_fh, &np))
539 return (error);
540 vp = NFSTOV(np);
541 vp->v_type = VDIR;
542 vp->v_flag = VROOT;
543 *vpp = vp;
544 return (0);
545}
546
547extern int syncprt;
548
549/*
550 * Flush out the buffer cache
551 */
552/* ARGSUSED */
78ed81a3 553int
15637ed4
RG
554nfs_sync(mp, waitfor)
555 struct mount *mp;
556 int waitfor;
557{
558 if (syncprt)
559 bufstats();
560 /*
561 * Force stale buffer cache information to be flushed.
562 */
563 mntflushbuf(mp, waitfor == MNT_WAIT ? B_SYNC : 0);
564 return (0);
565}
566
567/*
568 * At this point, this should never happen
569 */
570/* ARGSUSED */
78ed81a3 571int
15637ed4
RG
572nfs_fhtovp(mp, fhp, vpp)
573 struct mount *mp;
574 struct fid *fhp;
575 struct vnode **vpp;
576{
577
578 return (EINVAL);
579}
580
581/*
582 * Vnode pointer to File handle, should never happen either
583 */
584/* ARGSUSED */
78ed81a3 585int
15637ed4
RG
586nfs_vptofh(vp, fhp)
587 struct vnode *vp;
588 struct fid *fhp;
589{
590
591 return (EINVAL);
592}
593
594/*
595 * Vfs start routine, a no-op.
596 */
597/* ARGSUSED */
78ed81a3 598int
15637ed4
RG
599nfs_start(mp, flags, p)
600 struct mount *mp;
601 int flags;
602 struct proc *p;
603{
604
605 return (0);
606}
607
608/*
609 * Do operations associated with quotas, not supported
610 */
78ed81a3 611int
15637ed4
RG
612nfs_quotactl(mp, cmd, uid, arg, p)
613 struct mount *mp;
614 int cmd;
615 uid_t uid;
616 caddr_t arg;
617 struct proc *p;
618{
619#ifdef lint
620 mp = mp; cmd = cmd; uid = uid; arg = arg;
621#endif /* lint */
622 return (EOPNOTSUPP);
623}