386BSD 0.1 development
[unix-history] / usr / src / sys.386bsd / nfs / nfs_syscalls.c
/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* 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
* are met:
* 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
* SUCH DAMAGE.
*
* @(#)nfs_syscalls.c 7.26 (Berkeley) 4/16/91
*/
#include "param.h"
#include "systm.h"
#include "kernel.h"
#include "file.h"
#include "stat.h"
#include "namei.h"
#include "vnode.h"
#include "mount.h"
#include "proc.h"
#include "malloc.h"
#include "buf.h"
#include "mbuf.h"
#include "socket.h"
#include "socketvar.h"
#include "domain.h"
#include "protosw.h"
#include "../netinet/in.h"
#include "../netinet/tcp.h"
#include "nfsv2.h"
#include "nfs.h"
#include "nfsrvcache.h"
/* Global defs. */
extern u_long nfs_prog, nfs_vers;
extern int (*nfsrv_procs[NFS_NPROCS])();
extern struct buf nfs_bqueue;
extern int nfs_numasync;
extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
extern int nfs_tcpnodelay;
struct mbuf *nfs_compress();
#define TRUE 1
#define FALSE 0
static int nfs_asyncdaemon[NFS_MAXASYNCDAEMON];
static int compressreply[NFS_NPROCS] = {
FALSE,
TRUE,
TRUE,
FALSE,
TRUE,
TRUE,
FALSE,
FALSE,
TRUE,
TRUE,
TRUE,
TRUE,
TRUE,
TRUE,
TRUE,
TRUE,
TRUE,
TRUE,
};
/*
* NFS server system calls
* getfh() lives here too, but maybe should move to kern/vfs_syscalls.c
*/
/*
* Get file handle system call
*/
/* ARGSUSED */
getfh(p, uap, retval)
struct proc *p;
register struct args {
char *fname;
fhandle_t *fhp;
} *uap;
int *retval;
{
register struct nameidata *ndp;
register struct vnode *vp;
fhandle_t fh;
int error;
struct nameidata nd;
/*
* Must be super user
*/
if (error = suser(p->p_ucred, &p->p_acflag))
return (error);
ndp = &nd;
ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW;
ndp->ni_segflg = UIO_USERSPACE;
ndp->ni_dirp = uap->fname;
if (error = namei(ndp, p))
return (error);
vp = ndp->ni_vp;
bzero((caddr_t)&fh, sizeof(fh));
fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid;
error = VFS_VPTOFH(vp, &fh.fh_fid);
vput(vp);
if (error)
return (error);
error = copyout((caddr_t)&fh, (caddr_t)uap->fhp, sizeof (fh));
return (error);
}
/*
* Nfs server psuedo system call for the nfsd's
* Never returns unless it fails or gets killed
*/
/* ARGSUSED */
nfssvc(p, uap, retval)
struct proc *p;
register struct args {
int s;
caddr_t mskval;
int msklen;
caddr_t mtchval;
int mtchlen;
} *uap;
int *retval;
{
register struct mbuf *m;
register int siz;
register struct ucred *cr;
struct file *fp;
struct mbuf *mreq, *mrep, *nam, *md;
struct mbuf msk, mtch;
struct socket *so;
caddr_t dpos;
int procid, repstat, error, cacherep, wascomp;
u_long retxid;
/*
* Must be super user
*/
if (error = suser(p->p_ucred, &p->p_acflag))
return (error);
if (error = getsock(p->p_fd, uap->s, &fp))
return (error);
so = (struct socket *)fp->f_data;
if (sosendallatonce(so))
siz = NFS_MAXPACKET;
else
siz = NFS_MAXPACKET + sizeof(u_long);
if (error = soreserve(so, siz, siz))
goto bad;
if (error = sockargs(&nam, uap->mskval, uap->msklen, MT_SONAME))
goto bad;
bcopy((caddr_t)nam, (caddr_t)&msk, sizeof (struct mbuf));
msk.m_data = msk.m_dat;
m_freem(nam);
if (error = sockargs(&nam, uap->mtchval, uap->mtchlen, MT_SONAME))
goto bad;
bcopy((caddr_t)nam, (caddr_t)&mtch, sizeof (struct mbuf));
mtch.m_data = mtch.m_dat;
m_freem(nam);
/* Copy the cred so others don't see changes */
cr = p->p_ucred = crcopy(p->p_ucred);
/*
* 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_proto->pr_flags & PR_CONNREQUIRED) {
MGET(m, M_WAIT, MT_SOOPTS);
*mtod(m, int *) = 1;
m->m_len = sizeof(int);
sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m);
}
if (so->so_proto->pr_domain->dom_family == AF_INET &&
so->so_proto->pr_protocol == IPPROTO_TCP &&
nfs_tcpnodelay) {
MGET(m, M_WAIT, MT_SOOPTS);
*mtod(m, int *) = 1;
m->m_len = sizeof(int);
sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m);
}
so->so_rcv.sb_flags &= ~SB_NOINTR;
so->so_rcv.sb_timeo = 0;
so->so_snd.sb_flags &= ~SB_NOINTR;
so->so_snd.sb_timeo = 0;
/*
* Just loop around doin our stuff until SIGKILL
*/
for (;;) {
if (error = nfs_getreq(so, nfs_prog, nfs_vers, NFS_NPROCS-1,
&nam, &mrep, &md, &dpos, &retxid, &procid, cr,
&msk, &mtch, &wascomp)) {
if (nam)
m_freem(nam);
if (error == EPIPE || error == EINTR ||
error == ERESTART) {
error = 0;
goto bad;
}
so->so_error = 0;
continue;
}
if (nam)
cacherep = nfsrv_getcache(nam, retxid, procid, &mreq);
else
cacherep = RC_DOIT;
switch (cacherep) {
case RC_DOIT:
if (error = (*(nfsrv_procs[procid]))(mrep, md, dpos,
cr, retxid, &mreq, &repstat, p)) {
nfsstats.srv_errs++;
if (nam) {
nfsrv_updatecache(nam, retxid, procid,
FALSE, repstat, mreq);
m_freem(nam);
}
break;
}
nfsstats.srvrpccnt[procid]++;
if (nam)
nfsrv_updatecache(nam, retxid, procid, TRUE,
repstat, mreq);
mrep = (struct mbuf *)0;
case RC_REPLY:
m = mreq;
siz = 0;
while (m) {
siz += m->m_len;
m = m->m_next;
}
if (siz <= 0 || siz > NFS_MAXPACKET) {
printf("mbuf siz=%d\n",siz);
panic("Bad nfs svc reply");
}
mreq->m_pkthdr.len = siz;
mreq->m_pkthdr.rcvif = (struct ifnet *)0;
if (wascomp && compressreply[procid]) {
mreq = nfs_compress(mreq);
siz = mreq->m_pkthdr.len;
}
/*
* For non-atomic protocols, prepend a Sun RPC
* Record Mark.
*/
if (!sosendallatonce(so)) {
M_PREPEND(mreq, sizeof(u_long), M_WAIT);
*mtod(mreq, u_long *) = htonl(0x80000000 | siz);
}
error = nfs_send(so, nam, mreq, (struct nfsreq *)0);
if (nam)
m_freem(nam);
if (mrep)
m_freem(mrep);
if (error) {
if (error == EPIPE || error == EINTR ||
error == ERESTART)
goto bad;
so->so_error = 0;
}
break;
case RC_DROPIT:
m_freem(mrep);
m_freem(nam);
break;
};
}
bad:
return (error);
}
/*
* Nfs pseudo system call for asynchronous i/o daemons.
* These babies just pretend to be disk interrupt service routines
* for client nfs. They are mainly here for read ahead/write behind.
* Never returns unless it fails or gets killed
*/
/* ARGSUSED */
async_daemon(p, uap, retval)
struct proc *p;
struct args *uap;
int *retval;
{
register struct buf *bp, *dp;
register int i, myiod;
int error;
/*
* Must be super user
*/
if (error = suser(p->p_ucred, &p->p_acflag))
return (error);
/*
* Assign my position or return error if too many already running
*/
myiod = -1;
for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
if (nfs_asyncdaemon[i] == 0) {
nfs_asyncdaemon[i]++;
myiod = i;
break;
}
if (myiod == -1)
return (EBUSY);
nfs_numasync++;
dp = &nfs_bqueue;
/*
* Just loop around doin our stuff until SIGKILL
*/
for (;;) {
while (dp->b_actf == NULL && error == 0) {
nfs_iodwant[myiod] = p;
error = tsleep((caddr_t)&nfs_iodwant[myiod],
PWAIT | PCATCH, "nfsidl", 0);
nfs_iodwant[myiod] = (struct proc *)0;
}
while (dp->b_actf != NULL) {
/* Take one off the end of the list */
bp = dp->b_actl;
if (bp->b_actl == dp) {
dp->b_actf = dp->b_actl = (struct buf *)0;
} else {
dp->b_actl = bp->b_actl;
bp->b_actl->b_actf = dp;
}
(void) nfs_doio(bp);
}
if (error) {
nfs_asyncdaemon[myiod] = 0;
nfs_numasync--;
return (error);
}
}
}