* 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.
* %sccs.include.redist.c%
* @(#)nfs_vfsops.c 7.48 (Berkeley) %G%
#include <nfs/nfsmount.h>
#include <nfs/xdr_subs.h>
#include <nfs/nfsm_subs.h>
#include <nfs/nfsdiskless.h>
struct vfsops nfs_vfsops
= {
* This structure must be filled in by a primary bootstrap or bootstrap
* server for a diskless/dataless machine. It is initialized below just
* to ensure that it is allocated to initialized data (.data not .bss).
struct nfs_diskless nfs_diskless
= { 0 };
extern u_long nfs_procids
[NFS_NPROCS
];
extern u_long nfs_prog
, nfs_vers
;
void nfs_disconnect
__P((struct nfsmount
*));
void nfsargs_ntoh
__P((struct nfs_args
*));
static struct mount
*nfs_mountdiskless
__P((char *, char *, int,
struct sockaddr_in
*, struct nfs_args
*, register struct vnode
**));
register struct statfs
*sbp
;
register struct vnode
*vp
;
register struct nfsv2_statfs
*sfp
;
struct mbuf
*mreq
, *mrep
, *md
, *mb
, *mb2
;
isnq
= (nmp
->nm_flag
& NFSMNT_NQNFS
);
if (error
= nfs_nget(mp
, &nmp
->nm_fh
, &np
))
nfsstats
.rpccnt
[NFSPROC_STATFS
]++;
nfsm_reqhead(vp
, NFSPROC_STATFS
, NFSX_FH
);
nfsm_request(vp
, NFSPROC_STATFS
, p
, cred
);
nfsm_dissect(sfp
, struct nfsv2_statfs
*, NFSX_STATFS(isnq
));
sbp
->f_flags
= nmp
->nm_flag
;
sbp
->f_iosize
= NFS_MAXDGRAMDATA
;
sbp
->f_bsize
= 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
);
sbp
->f_files
= fxdr_unsigned(long, sfp
->sf_files
);
sbp
->f_ffree
= fxdr_unsigned(long, sfp
->sf_ffree
);
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
* - hand craft the swap nfs vnode hanging off a fake mount point
* if swdevt[0].sw_dev == NODEV
* - build the rootfs mount point and call mountnfs() to do the rest.
register struct mount
*mp
;
register struct nfs_diskless
*nd
= &nfs_diskless
;
struct proc
*p
= curproc
; /* XXX */
* XXX time must be non-zero when we init the interface or else
* the arp code will wedge...
/* Set up swap credentials. */
*proc0
.p_ucred
= nfs_diskless
.swap_ucred
;
* Do enough of ifconfig(8) so that the critical net interface can
if (error
= socreate(nd
->myif
.ifra_addr
.sa_family
, &so
, SOCK_DGRAM
, 0))
panic("nfs_mountroot: socreate: %d", error
);
if (error
= ifioctl(so
, SIOCAIFADDR
, (caddr_t
)&nd
->myif
, p
))
panic("nfs_mountroot: SIOCAIFADDR: %d", error
);
* If the gateway field is filled in, set it as the default route.
if (nd
->mygateway
.sin_len
!= 0) {
extern struct sockaddr_in icmpmask
;
sin
.sin_len
= sizeof (struct sockaddr_in
);
sin
.sin_family
= AF_INET
;
sin
.sin_addr
.s_addr
= 0; /* default */
in_sockmaskof(sin
.sin_addr
, &icmpmask
);
if (error
= rtrequest(RTM_ADD
, (struct sockaddr
*)&sin
,
(struct sockaddr
*)&nd
->mygateway
,
(struct sockaddr
*)&icmpmask
,
RTF_UP
| RTF_GATEWAY
, (struct rtentry
**)0))
panic("nfs_mountroot: RTM_ADD: %d", error
);
* 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
) {
nd
->swap_args
.fh
= (nfsv2fh_t
*)nd
->swap_fh
;
(void) nfs_mountdiskless(nd
->swap_hostnam
, "/swap", 0,
&nd
->swap_saddr
, &nd
->swap_args
, &vp
);
* Since the swap file is not the root dir of a file system,
* hack it to a regular file.
swdevt
[0].sw_nblks
= ntohl(nd
->swap_nblks
);
} else if (bdevvp(swapdev
, &swapdev_vp
))
panic("nfs_mountroot: can't setup swapdev_vp");
* Create the rootfs mount point.
nd
->root_args
.fh
= (nfsv2fh_t
*)nd
->root_fh
;
mp
= nfs_mountdiskless(nd
->root_hostnam
, "/", MNT_RDONLY
,
&nd
->root_saddr
, &nd
->root_args
, &vp
);
panic("nfs_mountroot: vfs_lock");
mp
->mnt_vnodecovered
= NULLVP
;
* This is not really an nfs issue, but it is much easier to
* set hostname here and then let the "/etc/rc.xxx" files
* mount the right /var based upon its preset value.
bcopy(nd
->my_hostnam
, hostname
, MAXHOSTNAMELEN
);
hostname
[MAXHOSTNAMELEN
- 1] = '\0';
for (i
= 0; i
< MAXHOSTNAMELEN
; i
++)
inittodr(nfs_diskless
.root_time
);
* Internal version of mount system call for diskless setup.
nfs_mountdiskless(path
, which
, mountflag
, sin
, args
, vpp
)
register struct vnode
**vpp
;
register struct mount
*mp
;
mp
= (struct mount
*)malloc((u_long
)sizeof(struct mount
),
panic("nfs_mountroot: %s mount malloc", which
);
mp
->mnt_op
= &nfs_vfsops
;
mp
->mnt_flag
= mountflag
;
MGET(m
, MT_SONAME
, M_DONTWAIT
);
panic("nfs_mountroot: %s mount mbuf", which
);
bcopy((caddr_t
)sin
, mtod(m
, caddr_t
), sin
->sin_len
);
if (error
= mountnfs(args
, mp
, m
, which
, path
, vpp
))
panic("nfs_mountroot: mount %s on %s: %d", path
, which
, error
);
* Convert the integer fields of the nfs_args structure from net byte order
* to host byte order. Called by nfs_mountroot() above.
register struct nfs_args
*nfsp
;
NTOHL(nfsp
->maxgrouplist
);
* 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 (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
,
args
.addrlen
, 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
;
if (mp
->mnt_flag
& MNT_UPDATE
) {
/* update paths, file handles, etc, here XXX */
MALLOC(nmp
, struct nfsmount
*, sizeof (struct nfsmount
),
bzero((caddr_t
)nmp
, sizeof (struct nfsmount
));
mp
->mnt_data
= (qaddr_t
)nmp
;
getnewfsid(mp
, MOUNT_NFS
);
nmp
->nm_flag
= argp
->flags
;
if ((nmp
->nm_flag
& (NFSMNT_NQNFS
| NFSMNT_MYWRITE
)) ==
(NFSMNT_NQNFS
| NFSMNT_MYWRITE
)) {
if (nmp
->nm_flag
& (NFSMNT_RDIRALOOK
| NFSMNT_LEASETERM
)) {
if ((nmp
->nm_flag
& NFSMNT_NQNFS
) == 0) {
* We have to set mnt_maxsymlink to a non-zero value so
* that COMPAT_43 routines will know that we are setting
* the d_type field in directories (and can zero it for
* unsuspecting binaries).
mp
->mnt_maxsymlinklen
= 1;
nmp
->nm_timeo
= NFS_TIMEO
;
nmp
->nm_retry
= NFS_RETRANS
;
nmp
->nm_wsize
= NFS_WSIZE
;
nmp
->nm_rsize
= NFS_RSIZE
;
nmp
->nm_numgrps
= NFS_MAXGRPS
;
nmp
->nm_readahead
= NFS_DEFRAHEAD
;
nmp
->nm_leaseterm
= NQ_DEFLEASE
;
nmp
->nm_deadthresh
= NQ_DEADTHRESH
;
nmp
->nm_tnext
= (struct nfsnode
*)nmp
;
nmp
->nm_tprev
= (struct nfsnode
*)nmp
;
bcopy((caddr_t
)argp
->fh
, (caddr_t
)&nmp
->nm_fh
, sizeof(nfsv2fh_t
));
mp
->mnt_stat
.f_type
= MOUNT_NFS
;
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_timeo
= (argp
->timeo
* NFS_HZ
+ 5) / 10;
if (nmp
->nm_timeo
< NFS_MINTIMEO
)
nmp
->nm_timeo
= NFS_MINTIMEO
;
else if (nmp
->nm_timeo
> NFS_MAXTIMEO
)
nmp
->nm_timeo
= NFS_MAXTIMEO
;
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
;
if ((argp
->flags
& NFSMNT_MAXGRPS
) && argp
->maxgrouplist
>= 0 &&
argp
->maxgrouplist
<= NFS_MAXGRPS
)
nmp
->nm_numgrps
= argp
->maxgrouplist
;
if ((argp
->flags
& NFSMNT_READAHEAD
) && argp
->readahead
>= 0 &&
argp
->readahead
<= NFS_MAXRAHEAD
)
nmp
->nm_readahead
= argp
->readahead
;
if ((argp
->flags
& NFSMNT_LEASETERM
) && argp
->leaseterm
>= 2 &&
argp
->leaseterm
<= NQ_MAXLEASE
)
nmp
->nm_leaseterm
= argp
->leaseterm
;
if ((argp
->flags
& NFSMNT_DEADTHRESH
) && argp
->deadthresh
>= 1 &&
argp
->deadthresh
<= NQ_NEVERDEAD
)
nmp
->nm_deadthresh
= argp
->deadthresh
;
/* Set up the sockets and per-host congestion */
nmp
->nm_sotype
= argp
->sotype
;
nmp
->nm_soproto
= argp
->proto
;
* For Connection based sockets (TCP,...) defer the connect until
* the first request, in case the server is not responding.
if (nmp
->nm_sotype
== SOCK_DGRAM
&&
(error
= nfs_connect(nmp
, (struct nfsreq
*)0)))
* This is silly, but it has to be set so that vinifod() works.
* We do not want to do an nfs_statfs() here since we can get
* stuck on a dead server and we are holding a lock on the mount
mp
->mnt_stat
.f_iosize
= NFS_MAXDGRAMDATA
;
* 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
))
free((caddr_t
)nmp
, M_NFSMNT
);
nfs_unmount(mp
, mntflags
, p
)
register struct nfsmount
*nmp
;
if (mntflags
& MNT_FORCE
) {
if (!doforce
|| mp
== rootfs
)
* 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) {
* Must handshake with nqnfs_clientd() if it is active.
nmp
->nm_flag
|= NFSMNT_DISMINPROG
;
while (nmp
->nm_inprog
!= NULLVP
)
(void) tsleep((caddr_t
)&lbolt
, PSOCK
, "nfsdism", 0);
if (error
= vflush(mp
, vp
, flags
)) {
nmp
->nm_flag
&= ~NFSMNT_DISMINPROG
;
* We are now committed to the unmount.
* For NQNFS, let the server daemon free the nfsmount structure.
if (nmp
->nm_flag
& (NFSMNT_NQNFS
| NFSMNT_KERB
))
nmp
->nm_flag
|= NFSMNT_DISMNT
;
* There are two reference counts to get rid of here.
if ((nmp
->nm_flag
& (NFSMNT_NQNFS
| NFSMNT_KERB
)) == 0)
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
nfs_sync(mp
, waitfor
, cred
, p
)
register struct vnode
*vp
;
* Force stale buffer cache information to be flushed.
for (vp
= mp
->mnt_mounth
; vp
; vp
= vp
->v_mountf
) {
* If the vnode that we are about to sync is no longer
* associated with this mount point, start over.
if (VOP_ISLOCKED(vp
) || vp
->v_dirtyblkhd
.le_next
== NULL
)
if (error
= VOP_FSYNC(vp
, cred
, waitfor
, p
))
* NFS flat namespace lookup.
* At this point, this should never happen
nfs_fhtovp(mp
, fhp
, nam
, vpp
, exflagsp
, credanonp
)
register struct mount
*mp
;
struct ucred
**credanonp
;
* 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
)