* Copyright (c) 1989 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Rick Macklem at The University of Guelph.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* @(#)nfs_vfsops.c 7.31 (Berkeley) 5/6/91
#include "../net/route.h"
#include "../netinet/in.h"
struct vfsops nfs_vfsops
= {
extern u_long nfs_procids
[NFS_NPROCS
];
extern u_long nfs_prog
, nfs_vers
;
struct nfs_diskless nfs_diskless
;
register struct statfs
*sbp
;
register struct vnode
*vp
;
register struct nfsv2_statfs
*sfp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
if (error
= nfs_nget(mp
, &nmp
->nm_fh
, &np
))
nfsstats
.rpccnt
[NFSPROC_STATFS
]++;
nfsm_reqhead(nfs_procids
[NFSPROC_STATFS
], cred
, NFSX_FH
);
nfsm_request(vp
, NFSPROC_STATFS
, p
, 0);
nfsm_disect(sfp
, struct nfsv2_statfs
*, NFSX_STATFS
);
sbp
->f_flags
= nmp
->nm_flag
;
sbp
->f_bsize
= fxdr_unsigned(long, sfp
->sf_tsize
);
sbp
->f_fsize
= fxdr_unsigned(long, sfp
->sf_bsize
);
sbp
->f_blocks
= fxdr_unsigned(long, sfp
->sf_blocks
);
sbp
->f_bfree
= fxdr_unsigned(long, sfp
->sf_bfree
);
sbp
->f_bavail
= fxdr_unsigned(long, sfp
->sf_bavail
);
if (sbp
!= &mp
->mnt_stat
) {
bcopy(mp
->mnt_stat
.f_mntonname
, sbp
->f_mntonname
, MNAMELEN
);
bcopy(mp
->mnt_stat
.f_mntfromname
, sbp
->f_mntfromname
, MNAMELEN
);
* Mount a remote root fs via. nfs. This depends on the info in the
* nfs_diskless structure that has been filled in properly by some primary
* It goes something like this:
* - do enough of "ifconfig" by calling ifioctl() so that the system
* - If nfs_diskless.mygateway is filled in, use that address as
* (This is done the 4.3 way with rtioctl() and should be changed)
* - hand craft the swap nfs vnode hanging off a fake mount point
* - build the rootfs mount point and call mountnfs() to do the rest.
register struct mount
*mp
;
* Do enough of ifconfig(8) so that critical net interface can
if (socreate(nfs_diskless
.myif
.ifra_addr
.sa_family
, &so
, SOCK_DGRAM
, 0))
if (ifioctl(so
, SIOCAIFADDR
, &nfs_diskless
.myif
))
* If the gateway field is filled in, set it as the default route.
if (nfs_diskless
.mygateway
.sa_family
== AF_INET
) {
sin
= (struct sockaddr_in
*) &rt
.rt_dst
;
sin
->sin_len
= sizeof (struct sockaddr_in
);
sin
->sin_family
= AF_INET
;
sin
->sin_addr
.s_addr
= 0; /* default */
bcopy((caddr_t
)&nfs_diskless
.mygateway
, (caddr_t
)&rt
.rt_gateway
,
sizeof (struct sockaddr_in
));
rt
.rt_flags
= (RTF_UP
| RTF_GATEWAY
);
if (rtioctl(SIOCADDRT
, (caddr_t
)&rt
))
* If swapping to an nfs node (indicated by swdevt[0].sw_dev == NODEV):
* Create a fake mount point just for the swap vnode so that the
* swap file can be on a different server from the rootfs.
if (swdevt
[0].sw_dev
== NODEV
) {
mp
= (struct mount
*)malloc((u_long
)sizeof(struct mount
),
mp
->mnt_op
= &nfs_vfsops
;
* Set up the diskless nfs_args for the swap mount point
* and then call mountnfs() to mount it.
* Since the swap file is not the root dir of a file system,
* hack it to a regular file.
nfs_diskless
.swap_args
.fh
= (nfsv2fh_t
*)nfs_diskless
.swap_fh
;
MGET(m
, MT_SONAME
, M_DONTWAIT
);
bcopy((caddr_t
)&nfs_diskless
.swap_saddr
, mtod(m
, caddr_t
),
nfs_diskless
.swap_saddr
.sa_len
);
m
->m_len
= nfs_diskless
.swap_saddr
.sa_len
;
if (mountnfs(&nfs_diskless
.swap_args
, mp
, m
, "/swap",
nfs_diskless
.swap_hostnam
, &vp
))
* Create the rootfs mount point.
mp
= (struct mount
*)malloc((u_long
)sizeof(struct mount
),
panic("nfs root mount2");
mp
->mnt_op
= &nfs_vfsops
;
mp
->mnt_flag
= MNT_RDONLY
;
* Set up the root fs args and call mountnfs() to do the rest.
nfs_diskless
.root_args
.fh
= (nfsv2fh_t
*)nfs_diskless
.root_fh
;
MGET(m
, MT_SONAME
, M_DONTWAIT
);
bcopy((caddr_t
)&nfs_diskless
.root_saddr
, mtod(m
, caddr_t
),
nfs_diskless
.root_saddr
.sa_len
);
m
->m_len
= nfs_diskless
.root_saddr
.sa_len
;
if (mountnfs(&nfs_diskless
.root_args
, mp
, m
, "/",
nfs_diskless
.root_hostnam
, &vp
))
mp
->mnt_vnodecovered
= NULLVP
;
inittodr((time_t)0); /* There is no time in the nfs fsstat so ?? */
* It seems a bit dumb to copyinstr() the host and path here and then
* bcopy() them in mountnfs(), but I wanted to detect errors before
* doing the sockargs() call because sockargs() allocates an mbuf and
* an error after that means that I have to release the mbuf.
nfs_mount(mp
, path
, data
, ndp
, p
)
char pth
[MNAMELEN
], hst
[MNAMELEN
];
if (mp
->mnt_flag
& MNT_UPDATE
)
if (error
= copyin(data
, (caddr_t
)&args
, sizeof (struct nfs_args
)))
if (error
= copyin((caddr_t
)args
.fh
, (caddr_t
)&nfh
, sizeof (nfsv2fh_t
)))
if (error
= copyinstr(path
, pth
, MNAMELEN
-1, &len
))
bzero(&pth
[len
], MNAMELEN
- len
);
if (error
= copyinstr(args
.hostname
, hst
, MNAMELEN
-1, &len
))
bzero(&hst
[len
], MNAMELEN
- len
);
/* sockargs() call must be after above copyin() calls */
if (error
= sockargs(&nam
, (caddr_t
)args
.addr
,
sizeof (struct sockaddr
), MT_SONAME
))
error
= mountnfs(&args
, mp
, nam
, pth
, hst
, &vp
);
* Common code for mount and mountroot
mountnfs(argp
, mp
, nam
, pth
, hst
, vpp
)
register struct nfs_args
*argp
;
register struct mount
*mp
;
register struct nfsmount
*nmp
;
struct proc
*p
= curproc
; /* XXX */
MALLOC(nmp
, struct nfsmount
*, sizeof *nmp
, M_NFSMNT
, M_WAITOK
);
bzero((caddr_t
)nmp
, sizeof *nmp
);
mp
->mnt_data
= (qaddr_t
)nmp
;
* Generate a unique nfs mount id. The problem is that a dev number
* is not unique across multiple systems. The techique is as follows:
* 1) Set to nblkdev,0 which will never be used otherwise
* 2) Generate a first guess as nblkdev,nfs_mntid where nfs_mntid is
* 3) Loop searching the mount list for another one with same id
* If a match, increment val[0] and try again
* NB: I increment val[0] { a long } instead of nfs_mntid { a u_char }
* so that nfs is not limited to 255 mount points
* Incrementing the high order bits does no real harm, since it
* simply makes the major dev number tick up. The upper bound is
* set to major dev 127 to avoid any sign extention problems
mp
->mnt_stat
.f_fsid
.val
[0] = makedev(nblkdev
, 0);
mp
->mnt_stat
.f_fsid
.val
[1] = MOUNT_NFS
;
tfsid
.val
[0] = makedev(nblkdev
, nfs_mntid
);
tfsid
.val
[1] = MOUNT_NFS
;
while (rootfs
&& getvfs(&tfsid
)) {
if (major(tfsid
.val
[0]) > 127) {
mp
->mnt_stat
.f_fsid
.val
[0] = tfsid
.val
[0];
nmp
->nm_flag
= argp
->flags
;
nmp
->nm_rttvar
= nmp
->nm_rto
<< 1;
nmp
->nm_retry
= NFS_RETRANS
;
nmp
->nm_wsize
= NFS_WSIZE
;
nmp
->nm_rsize
= NFS_RSIZE
;
bcopy((caddr_t
)argp
->fh
, (caddr_t
)&nmp
->nm_fh
, sizeof(nfsv2fh_t
));
bcopy(hst
, mp
->mnt_stat
.f_mntfromname
, MNAMELEN
);
bcopy(pth
, mp
->mnt_stat
.f_mntonname
, MNAMELEN
);
if ((argp
->flags
& NFSMNT_TIMEO
) && argp
->timeo
> 0) {
nmp
->nm_rto
= argp
->timeo
;
/* NFS timeouts are specified in 1/10 sec. */
nmp
->nm_rto
= (nmp
->nm_rto
* 10) / NFS_HZ
;
if (nmp
->nm_rto
< NFS_MINTIMEO
)
nmp
->nm_rto
= NFS_MINTIMEO
;
else if (nmp
->nm_rto
> NFS_MAXTIMEO
)
nmp
->nm_rto
= NFS_MAXTIMEO
;
nmp
->nm_rttvar
= nmp
->nm_rto
<< 1;
if ((argp
->flags
& NFSMNT_RETRANS
) && argp
->retrans
> 1) {
nmp
->nm_retry
= argp
->retrans
;
if (nmp
->nm_retry
> NFS_MAXREXMIT
)
nmp
->nm_retry
= NFS_MAXREXMIT
;
if ((argp
->flags
& NFSMNT_WSIZE
) && argp
->wsize
> 0) {
nmp
->nm_wsize
= argp
->wsize
;
/* Round down to multiple of blocksize */
else if (nmp
->nm_wsize
> NFS_MAXDATA
)
nmp
->nm_wsize
= NFS_MAXDATA
;
if (nmp
->nm_wsize
> MAXBSIZE
)
nmp
->nm_wsize
= MAXBSIZE
;
if ((argp
->flags
& NFSMNT_RSIZE
) && argp
->rsize
> 0) {
nmp
->nm_rsize
= argp
->rsize
;
/* Round down to multiple of blocksize */
else if (nmp
->nm_rsize
> NFS_MAXDATA
)
nmp
->nm_rsize
= NFS_MAXDATA
;
if (nmp
->nm_rsize
> MAXBSIZE
)
nmp
->nm_rsize
= MAXBSIZE
;
/* Set up the sockets and per-host congestion */
nmp
->nm_sotype
= argp
->sotype
;
nmp
->nm_soproto
= argp
->proto
;
if (error
= nfs_connect(nmp
))
if (error
= nfs_statfs(mp
, &mp
->mnt_stat
, p
))
* A reference count is needed on the nfsnode representing the
* remote root. If this object is not persistent, then backward
* traversals of the mount point (i.e. "..") will not work if
* the nfsnode gets flushed out of the cache. Ufs does not have
* this problem, because one can identify root inodes by their
if (error
= nfs_nget(mp
, &nmp
->nm_fh
, &np
))
* Unlock it, but keep the reference count.
nfs_unmount(mp
, mntflags
, p
)
register struct nfsmount
*nmp
;
if (mntflags
& MNT_FORCE
) {
if (!doforce
|| mp
== rootfs
)
* Clear out the buffer cache
* Goes something like this..
* - Check for activity on the root vnode (other than ourselves).
* - Call vflush() to clear out vnodes for this file system,
* except for the root vnode.
* - Decrement reference on the vnode representing remote root.
* - Free up the data structures
* We need to decrement the ref. count on the nfsnode representing
* the remote root. See comment in mountnfs(). The VFS unmount()
* has done vput on this vnode, otherwise we would get deadlock!
if (error
= nfs_nget(mp
, &nmp
->nm_fh
, &np
))
if (vp
->v_usecount
> 2) {
if (error
= vflush(mp
, vp
, flags
)) {
* Get rid of two reference counts, and unlock it on the second.
free((caddr_t
)nmp
, M_NFSMNT
);
* Return root of a filesystem
register struct vnode
*vp
;
if (error
= nfs_nget(mp
, &nmp
->nm_fh
, &np
))
* Flush out the buffer cache
* Force stale buffer cache information to be flushed.
mntflushbuf(mp
, waitfor
== MNT_WAIT
? B_SYNC
: 0);
* At this point, this should never happen
* Vnode pointer to File handle, should never happen either
* Vfs start routine, a no-op.
* Do operations associated with quotas, not supported
nfs_quotactl(mp
, cmd
, uid
, arg
, p
)
mp
= mp
; cmd
= cmd
; uid
= uid
; arg
= arg
;