* 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_syscalls.c 7.35 (Berkeley) %G%
#include <sys/socketvar.h>
#include <nfs/nfsrvcache.h>
#include <nfs/nfsmount.h>
extern u_long nfs_prog
, nfs_vers
;
extern int (*nfsrv_procs
[NFS_NPROCS
])();
extern struct queue_entry nfs_bufq
;
extern struct proc
*nfs_iodwant
[NFS_MAXASYNCDAEMON
];
extern time_t nqnfsstarttime
;
extern struct nfsrv_req nsrvq_head
;
extern struct nfsd nfsd_head
;
extern int nqsrv_writeslack
;
struct nfssvc_sock
*nfs_udpsock
, *nfs_cltpsock
;
int nuidhash_max
= NFS_MAXUIDHASH
;
static int nfs_numnfsd
= 0;
static int notstarted
= 1;
static int modify_flag
= 0;
void nfsrv_cleancache(), nfsrv_rcv(), nfsrv_wakenfsd(), nfs_sndunlock();
void nfsrv_slpderef(), nfsrv_init();
static int nfs_asyncdaemon
[NFS_MAXASYNCDAEMON
];
* NFS server system calls
* getfh() lives here too, but maybe should move to kern/vfs_syscalls.c
* Get file handle system call
register struct getfh_args
*uap
;
register struct vnode
*vp
;
if (error
= suser(p
->p_ucred
, &p
->p_acflag
))
NDINIT(&nd
, LOOKUP
, FOLLOW
| LOCKLEAF
, UIO_USERSPACE
, uap
->fname
, p
);
bzero((caddr_t
)&fh
, sizeof(fh
));
fh
.fh_fsid
= vp
->v_mount
->mnt_stat
.f_fsid
;
error
= VFS_VPTOFH(vp
, &fh
.fh_fid
);
error
= copyout((caddr_t
)&fh
, (caddr_t
)uap
->fhp
, sizeof (fh
));
static struct nfssvc_sock nfssvc_sockhead
;
#define SLP_DEREFFREE 0x100
#define SLP_CLRFREE 0x200
* Nfs server psuedo system call for the nfsd's
* Based on the flag value it either:
* - adds a socket to the selection list
* - remains in the kernel as an nfsd
* - remains in the kernel as an nfsiod
register struct nfssvc_args
*uap
;
struct nfsd_args nfsdarg
;
struct nfsd_srvargs nfsd_srvargs
, *nsd
= &nfsd_srvargs
;
struct nfsuid
*nuidp
, **nuh
;
if (error
= suser(p
->p_ucred
, &p
->p_acflag
))
while (nfssvc_sockhead
.ns_flag
& SLP_INIT
) {
nfssvc_sockhead
.ns_flag
|= SLP_WANTINIT
;
(void) tsleep((caddr_t
)&nfssvc_sockhead
, PSOCK
, "nfsd init", 0);
if (uap
->flag
& NFSSVC_BIOD
)
else if (uap
->flag
& NFSSVC_MNTD
) {
if (error
= copyin(uap
->argp
, (caddr_t
)&ncd
, sizeof (ncd
)))
NDINIT(&nd
, LOOKUP
, FOLLOW
| LOCKLEAF
, UIO_USERSPACE
,
if ((nd
.ni_vp
->v_flag
& VROOT
) == 0)
nmp
= VFSTONFS(nd
.ni_vp
->v_mount
);
else if (nmp
->nm_flag
& NFSMNT_MNTD
)
nmp
->nm_flag
|= NFSMNT_MNTD
;
error
= nqnfs_clientd(nmp
, p
->p_ucred
, &ncd
, uap
->flag
,
} else if (uap
->flag
& NFSSVC_ADDSOCK
) {
if (error
= copyin(uap
->argp
, (caddr_t
)&nfsdarg
,
if (error
= getsock(p
->p_fd
, nfsdarg
.sock
, &fp
))
* Get the client address for connected sockets.
if (nfsdarg
.name
== NULL
|| nfsdarg
.namelen
== 0)
else if (error
= sockargs(&nam
, nfsdarg
.name
, nfsdarg
.namelen
,
error
= nfssvc_addsock(fp
, nam
);
if (error
= copyin(uap
->argp
, (caddr_t
)nsd
, sizeof (*nsd
)))
if ((uap
->flag
& NFSSVC_AUTHIN
) && (nfsd
= nsd
->nsd_nfsd
) &&
(nfsd
->nd_slp
->ns_flag
& SLP_VALID
)) {
if (slp
->ns_numuids
< nuidhash_max
) {
nuidp
= (struct nfsuid
*)
malloc(sizeof (struct nfsuid
), M_NFSUID
, M_WAITOK
);
nuidp
= (struct nfsuid
*)0;
if ((slp
->ns_flag
& SLP_VALID
) == 0) {
free((caddr_t
)nuidp
, M_NFSUID
);
if (nuidp
== (struct nfsuid
*)0) {
nuidp
->nu_hprev
->nu_hnext
= nuidp
->nu_hnext
;
nuidp
->nu_hnext
->nu_hprev
= nuidp
->nu_hprev
;
nuidp
->nu_cr
= nsd
->nsd_cr
;
nuidp
->nu_uid
= nsd
->nsd_uid
;
insque(nuidp
, (struct nfsuid
*)slp
);
nuh
= &slp
->ns_uidh
[NUIDHASH(nsd
->nsd_uid
)];
if (nuidp
->nu_hnext
= *nuh
)
nuidp
->nu_hnext
->nu_hprev
= nuidp
;
nuidp
->nu_hprev
= (struct nfsuid
*)0;
if ((uap
->flag
& NFSSVC_AUTHINFAIL
) && (nfsd
= nsd
->nsd_nfsd
))
nfsd
->nd_flag
|= NFSD_AUTHFAIL
;
error
= nfssvc_nfsd(nsd
, uap
->argp
, p
);
if (error
== EINTR
|| error
== ERESTART
)
* Adds a socket to the list for servicing by nfsds.
nfssvc_addsock(fp
, mynam
)
register struct nfssvc_sock
*slp
;
register struct socket
*so
;
struct nfssvc_sock
*tslp
;
so
= (struct socket
*)fp
->f_data
;
tslp
= (struct nfssvc_sock
*)0;
* Add it to the list, as required.
if (so
->so_proto
->pr_protocol
== IPPROTO_UDP
) {
if (tslp
->ns_flag
& SLP_VALID
) {
} else if (so
->so_proto
->pr_protocol
== ISOPROTO_CLTP
) {
if (tslp
->ns_flag
& SLP_VALID
) {
if (so
->so_type
== SOCK_STREAM
)
siz
= NFS_MAXPACKET
+ sizeof (u_long
);
if (error
= soreserve(so
, siz
, siz
)) {
* Set protocol specific options { for now TCP only } and
* reserve some space. For datagram sockets, this can get called
* repeatedly for the same socket, but that isn't harmful.
if (so
->so_type
== SOCK_STREAM
) {
MGET(m
, M_WAIT
, MT_SOOPTS
);
sosetopt(so
, SOL_SOCKET
, SO_KEEPALIVE
, m
);
if (so
->so_proto
->pr_domain
->dom_family
== AF_INET
&&
so
->so_proto
->pr_protocol
== IPPROTO_TCP
) {
MGET(m
, M_WAIT
, MT_SOOPTS
);
sosetopt(so
, IPPROTO_TCP
, TCP_NODELAY
, m
);
so
->so_rcv
.sb_flags
&= ~SB_NOINTR
;
so
->so_snd
.sb_flags
&= ~SB_NOINTR
;
slp
= (struct nfssvc_sock
*)
malloc(sizeof (struct nfssvc_sock
), M_NFSSVC
, M_WAITOK
);
printf("Alloc nfssvc_sock 0x%x\n", slp
);
bzero((caddr_t
)slp
, sizeof (struct nfssvc_sock
));
slp
->ns_prev
= nfssvc_sockhead
.ns_prev
;
slp
->ns_prev
->ns_next
= slp
;
slp
->ns_next
= &nfssvc_sockhead
;
nfssvc_sockhead
.ns_prev
= slp
;
slp
->ns_lrunext
= slp
->ns_lruprev
= (struct nfsuid
*)slp
;
so
->so_upcallarg
= (caddr_t
)slp
;
so
->so_upcall
= nfsrv_rcv
;
slp
->ns_flag
= (SLP_VALID
| SLP_NEEDQ
);
* Called by nfssvc() for nfsds. Just loops around servicing rpc requests
* until it is killed by a signal.
nfssvc_nfsd(nsd
, argp
, p
)
struct nfsd_srvargs
*nsd
;
register struct mbuf
*m
, *nam2
;
register struct nfssvc_sock
*slp
;
register struct socket
*so
;
struct nfssvc_sock
*oslp
;
struct nfsd
*nd
= nsd
->nsd_nfsd
;
if (nd
== (struct nfsd
*)0) {
nsd
->nsd_nfsd
= nd
= (struct nfsd
*)
malloc(sizeof (struct nfsd
), M_NFSD
, M_WAITOK
);
bzero((caddr_t
)nd
, sizeof (struct nfsd
));
* Loop getting rpc requests until SIGKILL.
if ((nd
->nd_flag
& NFSD_REQINPROG
) == 0) {
while (nd
->nd_slp
== (struct nfssvc_sock
*)0 &&
(nfsd_head
.nd_flag
& NFSD_CHECKSLP
) == 0) {
nd
->nd_flag
|= NFSD_WAITING
;
error
= tsleep((caddr_t
)nd
, PSOCK
| PCATCH
, "nfsd", 0);
if (nd
->nd_slp
== (struct nfssvc_sock
*)0 &&
(nfsd_head
.nd_flag
& NFSD_CHECKSLP
)) {
slp
= nfssvc_sockhead
.ns_next
;
while (slp
!= &nfssvc_sockhead
) {
if ((slp
->ns_flag
& (SLP_VALID
| SLP_DOREC
))
== (SLP_VALID
| SLP_DOREC
)) {
slp
->ns_flag
&= ~SLP_DOREC
;
if (slp
== &nfssvc_sockhead
)
nfsd_head
.nd_flag
&= ~NFSD_CHECKSLP
;
if ((slp
= nd
->nd_slp
) == (struct nfssvc_sock
*)0)
if (slp
->ns_flag
& SLP_VALID
) {
if (slp
->ns_flag
& SLP_DISCONN
)
else if (slp
->ns_flag
& SLP_NEEDQ
) {
slp
->ns_flag
&= ~SLP_NEEDQ
;
(void) nfs_sndlock(&slp
->ns_solock
,
nfsrv_rcv(slp
->ns_so
, (caddr_t
)slp
,
nfs_sndunlock(&slp
->ns_solock
);
error
= nfsrv_dorec(slp
, nd
);
nd
->nd_flag
|= NFSD_REQINPROG
;
if (error
|| (slp
->ns_flag
& SLP_VALID
) == 0) {
nd
->nd_slp
= (struct nfssvc_sock
*)0;
nd
->nd_flag
&= ~NFSD_REQINPROG
;
* Check to see if authorization is needed.
if (nd
->nd_flag
& NFSD_NEEDAUTH
) {
nd
->nd_flag
&= ~NFSD_NEEDAUTH
;
nsd
->nsd_uid
= nd
->nd_cr
.cr_uid
;
mtod(slp
->ns_nam
, struct sockaddr_in
*)->sin_addr
.s_addr
;
nsd
->nsd_authlen
= nd
->nd_authlen
;
(void) copyout(nd
->nd_authstr
, nsd
->nsd_authstr
,
(void) copyout((caddr_t
)nsd
, argp
, sizeof (*nsd
));
if (so
->so_proto
->pr_flags
& PR_CONNREQUIRED
)
solockp
= &slp
->ns_solock
;
* nam == nam2 for connectionless protocols such as UDP
* nam2 == NULL for connection based protocols to disable
* recent request caching.
cacherep
= nfsrv_getcache(nam2
, nd
, &mreq
);
* Check for just starting up for NQNFS and send
* fake "try again later" replies to the NQNFS clients.
if (notstarted
&& nqnfsstarttime
<= time
.tv_sec
) {
nqnfsstarttime
= time
.tv_sec
+ nqsrv_writeslack
;
if (nd
->nd_nqlflag
== NQL_NOVAL
)
else if (nd
->nd_procnum
!= NFSPROC_WRITE
) {
nd
->nd_procnum
= NFSPROC_NOOP
;
nd
->nd_repstat
= NQNFS_TRYLATER
;
} else if (nd
->nd_flag
& NFSD_AUTHFAIL
) {
nd
->nd_flag
&= ~NFSD_AUTHFAIL
;
nd
->nd_procnum
= NFSPROC_NOOP
;
nd
->nd_repstat
= NQNFS_AUTHERR
;
error
= (*(nfsrv_procs
[nd
->nd_procnum
]))(nd
,
nd
->nd_mrep
, nd
->nd_md
, nd
->nd_dpos
, &nd
->nd_cr
,
if (nd
->nd_cr
.cr_ref
!= 1) {
printf("nfssvc cref=%d\n", nd
->nd_cr
.cr_ref
);
if (nd
->nd_procnum
!= NQNFSPROC_VACATED
)
nfsrv_updatecache(nam2
, nd
, FALSE
, mreq
);
nfsstats
.srvrpccnt
[nd
->nd_procnum
]++;
nfsrv_updatecache(nam2
, nd
, TRUE
, mreq
);
nd
->nd_mrep
= (struct mbuf
*)0;
if (siz
<= 0 || siz
> NFS_MAXPACKET
) {
printf("mbuf siz=%d\n",siz
);
panic("Bad nfs svc reply");
m
->m_pkthdr
.rcvif
= (struct ifnet
*)0;
* For stream protocols, prepend a Sun RPC
if (sotype
== SOCK_STREAM
) {
M_PREPEND(m
, NFSX_UNSIGNED
, M_WAIT
);
*mtod(m
, u_long
*) = htonl(0x80000000 | siz
);
(void) nfs_sndlock(solockp
, (struct nfsreq
*)0);
if (slp
->ns_flag
& SLP_VALID
)
error
= nfs_send(so
, nam2
, m
, (struct nfsreq
*)0);
if (error
== EINTR
|| error
== ERESTART
) {
if (nfsrv_dorec(slp
, nd
)) {
nd
->nd_flag
&= ~NFSD_REQINPROG
;
nd
->nd_slp
= (struct nfssvc_sock
*)0;
panic("nfssvc: M_NAMEI");
panic("nfssvc: STARTSAVE");
free((caddr_t
)nd
, M_NFSD
);
nsd
->nsd_nfsd
= (struct nfsd
*)0;
nfsrv_init(TRUE
); /* Reinitialize everything */
* Asynchronous I/O daemons for client nfs.
* These babies just pretend to be disk interrupt service routines.
* They are mainly here for read ahead/write behind.
* Never returns unless it fails or gets killed.
register struct buf
*bp
, *dp
;
* Assign my position or return error if too many already running
for (i
= 0; i
< NFS_MAXASYNCDAEMON
; i
++)
if (nfs_asyncdaemon
[i
] == 0) {
* Just loop around doin our stuff until SIGKILL
while (nfs_bufq
.qe_next
== NULL
&& error
== 0) {
error
= tsleep((caddr_t
)&nfs_iodwant
[myiod
],
PWAIT
| PCATCH
, "nfsidl", 0);
nfs_iodwant
[myiod
] = (struct proc
*)0;
while ((bp
= nfs_bufq
.qe_next
) != NULL
) {
/* Take one off the front of the list */
queue_remove(&nfs_bufq
, bp
, struct buf
*, b_freelist
);
(void) nfs_doio(bp
, (struct proc
*)0);
nfs_asyncdaemon
[myiod
] = 0;
* Shut down a socket associated with an nfssvc_sock structure.
* Should be called with the send lock set, if required.
* The trick here is to increment the sref at the start, so that the nfsds
* will stop using it and clear ns_flag at the end so that it will not be
* reassigned during cleanup.
register struct nfssvc_sock
*slp
;
register struct nfsuid
*nuidp
, *onuidp
;
slp
->ns_flag
&= ~SLP_ALLFLAGS
;
slp
->ns_fp
= (struct file
*)0;
closef(fp
, (struct proc
*)0);
while (nuidp
!= (struct nfsuid
*)slp
) {
nuidp
= nuidp
->nu_lrunext
;
free((caddr_t
)onuidp
, M_NFSUID
);
slp
->ns_lrunext
= slp
->ns_lruprev
= (struct nfsuid
*)slp
;
for (i
= 0; i
< NUIDHASHSIZ
; i
++)
slp
->ns_uidh
[i
] = (struct nfsuid
*)0;
* Get an authorization string for the uid by having the mount_nfs sitting
* on this mount point porpous out of the kernel and do it.
nfs_getauth(nmp
, rep
, cred
, auth_type
, auth_str
, auth_len
)
register struct nfsmount
*nmp
;
while ((nmp
->nm_flag
& NFSMNT_WAITAUTH
) == 0) {
nmp
->nm_flag
|= NFSMNT_WANTAUTH
;
(void) tsleep((caddr_t
)&nmp
->nm_authtype
, PSOCK
,
if (error
= nfs_sigintr(nmp
, rep
, rep
->r_procp
)) {
nmp
->nm_flag
&= ~NFSMNT_WANTAUTH
;
nmp
->nm_flag
&= ~(NFSMNT_WAITAUTH
| NFSMNT_WANTAUTH
);
nmp
->nm_authstr
= *auth_str
= (char *)malloc(RPCAUTH_MAXSIZ
, M_TEMP
, M_WAITOK
);
nmp
->nm_authuid
= cred
->cr_uid
;
wakeup((caddr_t
)&nmp
->nm_authstr
);
* And wait for mount_nfs to do its stuff.
while ((nmp
->nm_flag
& NFSMNT_HASAUTH
) == 0 && error
== 0) {
(void) tsleep((caddr_t
)&nmp
->nm_authlen
, PSOCK
,
error
= nfs_sigintr(nmp
, rep
, rep
->r_procp
);
if (nmp
->nm_flag
& NFSMNT_AUTHERR
) {
nmp
->nm_flag
&= ~NFSMNT_AUTHERR
;
free((caddr_t
)*auth_str
, M_TEMP
);
*auth_type
= nmp
->nm_authtype
;
*auth_len
= nmp
->nm_authlen
;
nmp
->nm_flag
&= ~NFSMNT_HASAUTH
;
nmp
->nm_flag
|= NFSMNT_WAITAUTH
;
if (nmp
->nm_flag
& NFSMNT_WANTAUTH
) {
nmp
->nm_flag
&= ~NFSMNT_WANTAUTH
;
wakeup((caddr_t
)&nmp
->nm_authtype
);
* Derefence a server socket structure. If it has no more references and
* is no longer valid, you can throw it away.
register struct nfssvc_sock
*slp
;
if (--(slp
->ns_sref
) == 0 && (slp
->ns_flag
& SLP_VALID
) == 0) {
slp
->ns_prev
->ns_next
= slp
->ns_next
;
slp
->ns_next
->ns_prev
= slp
->ns_prev
;
free((caddr_t
)slp
, M_NFSSVC
);
if (slp
->ns_flag
& SLP_DEREFFREE
)
panic("deref dup free 0x%x of deref free\n", slp
);
slp
->ns_prev
->ns_next
= slp
->ns_next
;
slp
->ns_next
->ns_prev
= slp
->ns_prev
;
if (slp
->ns_flag
& SLP_CLRFREE
)
panic("deref dup free 0x%x of clrall free\n", slp
);
slp
->ns_flag
|= SLP_DEREFFREE
;
printf("Free deref sock 0x%x\n", slp
);
* Initialize the data structures for the server.
* Handshake with any new nfsds starting up to avoid any chance of
register struct nfssvc_sock
*slp
;
struct nfssvc_sock
*oslp
;
if (nfssvc_sockhead
.ns_flag
& SLP_INIT
)
nfssvc_sockhead
.ns_flag
|= SLP_INIT
;
slp
= nfssvc_sockhead
.ns_next
;
while (slp
!= &nfssvc_sockhead
) {
if (slp
->ns_flag
& SLP_VALID
)
slp
->ns_next
->ns_prev
= slp
->ns_prev
;
slp
->ns_prev
->ns_next
= slp
->ns_next
;
free((caddr_t
)oslp
, M_NFSSVC
);
if (oslp
->ns_flag
& SLP_DEREFFREE
)
panic("clrall dup free 0x%x of deref free\n",
if (oslp
->ns_flag
& SLP_CLRFREE
)
panic("clrall dup free 0x%x of clrall free\n",
oslp
->ns_flag
|= SLP_CLRFREE
;
printf("Free all socks 0x%x\n", oslp
);
nfsrv_cleancache(); /* And clear out server cache */
nfs_udpsock
= (struct nfssvc_sock
*)
malloc(sizeof (struct nfssvc_sock
), M_NFSSVC
, M_WAITOK
);
printf("Alloc nfs_udpsock 0x%x\n", nfs_udpsock
);
bzero((caddr_t
)nfs_udpsock
, sizeof (struct nfssvc_sock
));
nfs_cltpsock
= (struct nfssvc_sock
*)
malloc(sizeof (struct nfssvc_sock
), M_NFSSVC
, M_WAITOK
);
printf("Alloc nfs_cltpsock 0x%x\n", nfs_cltpsock
);
bzero((caddr_t
)nfs_cltpsock
, sizeof (struct nfssvc_sock
));
nfssvc_sockhead
.ns_next
= nfs_udpsock
;
nfs_udpsock
->ns_next
= nfs_cltpsock
;
nfs_cltpsock
->ns_next
= &nfssvc_sockhead
;
nfssvc_sockhead
.ns_prev
= nfs_cltpsock
;
nfs_cltpsock
->ns_prev
= nfs_udpsock
;
nfs_udpsock
->ns_prev
= &nfssvc_sockhead
;
nfs_udpsock
->ns_lrunext
= nfs_udpsock
->ns_lruprev
=
(struct nfsuid
*)nfs_udpsock
;
nfs_cltpsock
->ns_lrunext
= nfs_cltpsock
->ns_lruprev
=
(struct nfsuid
*)nfs_cltpsock
;
nfsd_head
.nd_next
= nfsd_head
.nd_prev
= &nfsd_head
;
nfssvc_sockhead
.ns_flag
&= ~SLP_INIT
;
if (nfssvc_sockhead
.ns_flag
& SLP_WANTINIT
) {
nfssvc_sockhead
.ns_flag
&= ~SLP_WANTINIT
;
wakeup((caddr_t
)&nfssvc_sockhead
);