V10/lsys/fs/netb.c
#include "sys/param.h"
#include "sys/user.h"
#include "sys/netb.h"
#include "sys/inode.h"
#include "sys/buf.h"
#include "sys/systm.h"
#include "sys/stat.h"
#include "sys/file.h"
#include "sys/conf.h"
#include "sys/nbio.h"
static long nbtrannum;
static int nbfstyp;
int nbdebug;
int nbput(), nbupdat(), nbread(), nbwrite(), nbtrunc(), nbstat();
int nbnami(), nbioctl(), nbmount(), nbdirread();
struct fstypsw nbfs =
fsinit(nbput, nbupdat, nbread, nbwrite, nbtrunc, nbstat,
nbnami, nbmount, nbioctl, nullopen, nbdirread);
unsigned char *getnbbuf();
#define NBMAXMSG (5*1024) /* probably enough */
int nbbsz = NBMAXMSG;
/*
* flags in i_un.i_fsflags
* valid only in the root inode
*/
#define NBUSY 01 /* already a message outstanding */
#define NWANT 02 /* someone waiting for BUSY */
#define NDEAD 04 /* communication line assumed dead */
/*
* flags valid in any netb inode
*/
#define NSTOK 010 /* inode data is fresh; skip next stat message */
nbput(ip)
register struct inode *ip;
{
unsigned char sbuf[SNBSIZE];
unsigned char rbuf[RNBSIZE];
register unsigned char *sp;
/* don't put root, as optimization. pointless? */
if (ip->i_mpoint->i_mroot == ip)
return;
if(ip->i_flag & ICHG) /* this is an extra message, but not often? */
nbupdat(ip, &time, &time, 0);
sp = sbuf;
bzero(sp, sizeof(sbuf));
tonetchar(sp, SNB_VERSION, NETB);
tonetchar(sp, SNB_CMD, NBPUT);
tonetlong(sp, SNB_LEN, sizeof(sbuf));
tonetlong(sp, SNB_TAG, ip->i_un.i_tag);
nbsend(ip, sp, rbuf, sizeof(rbuf));
}
nbtrunc(ip)
register struct inode *ip;
{
unsigned char sbuf[SNBSIZE];
unsigned char rbuf[RNBSIZE];
register unsigned char *sp;
ip->i_un.i_fsflags &=~ NSTOK;
sp = sbuf;
bzero(sp, sizeof(sbuf));
tonetchar(sp, SNB_VERSION, NETB);
tonetchar(sp, SNB_CMD, NBTRNC);
tonetlong(sp, SNB_LEN, sizeof(sbuf));
tonetlong(sp, SNB_TAG, ip->i_un.i_tag);
nbsend(ip, sp, rbuf, sizeof(rbuf));
}
/*
* the only times we're interested in are those which have waitfor == 0
* (or those which have ta or tb != &time)
*/
nbupdat(ip, ta, tb, waitfor)
register time_t *ta, *tb;
register struct inode *ip;
{
unsigned char sbuf[SUPSIZE];
unsigned char rbuf[RNBSIZE];
register unsigned char *sp;
ip->i_un.i_fsflags &=~ NSTOK;
sp = sbuf;
bzero(sp, sizeof(sbuf));
tonetchar(sp, SNB_VERSION, NETB);
tonetchar(sp, SNB_CMD, NBUPD);
tonetlong(sp, SNB_LEN, sizeof(sbuf));
tonetlong(sp, SNB_TAG, ip->i_un.i_tag);
tonetshort(sp, SUP_MODE, ip->i_mode);
/*
* in theory *ta or *tb could be incremented
* while we are copying them char-by-char
* in practice, it doesn't matter,
* as *t? != &time
*/
if((ta != &time) && (ip->i_flag & IACC))
tonetlong(sp, SUP_ATIME, *ta);
if((tb != &time) && (ip->i_flag & IUPD))
tonetlong(sp, SUP_MTIME, *tb);
nbsend(ip, sp, rbuf, sizeof(rbuf));
ip->i_flag &= ~(IUPD|IACC|ICHG);
}
/*
* if NSTOK is set,
* the inode is full of stat data from a recent namei
* for stat, NSTOK always set, because we just called namei
* (except for the root, for which namei isn't called)
* for fstat, it is probably not set
*/
nbstat(ip, ub)
register struct inode *ip;
struct stat *ub;
{
unsigned char sbuf[SSTSIZE];
unsigned char rbuf[RSTSIZE];
struct stat ds;
register unsigned char *p;
long now;
if ((ip->i_un.i_fsflags & NSTOK) == 0) {
p = sbuf;
bzero(p, sizeof(sbuf));
tonetchar(p, SNB_VERSION, NETB);
tonetchar(p, SNB_CMD, NBSTAT);
tonetlong(p, SNB_LEN, sizeof(sbuf));
tonetlong(p, SNB_TAG, ip->i_un.i_tag);
now = time; /* in case clock ticks */
tonetlong(p, SST_TIME, now);
nbsend(ip, p, rbuf, sizeof(rbuf));
if(u.u_error)
return;
p = rbuf;
ip->i_mode = frnetshort(p, RST_MODE);
ip->i_nlink = frnetshort(p, RST_NLINK);
ip->i_uid = frnetshort(p, RST_UID);
ip->i_gid = frnetshort(p, RST_GID);
ip->i_size = frnetlong(p, RST_SIZE);
ip->i_un.i_netatime = frnetlong(p, RST_ATIME);
ip->i_un.i_netmtime = frnetlong(p, RST_MTIME);
ip->i_un.i_netctime = frnetlong(p, RST_CTIME);
ip->i_un.i_netrdev = frnetlong(p, RST_RDEV);
}
ds.st_dev = ip->i_dev;
ds.st_ino = ip->i_number;
ds.st_mode = ip->i_mode;
ds.st_nlink = ip->i_nlink;
ds.st_uid = ip->i_uid;
ds.st_gid = ip->i_gid;
ds.st_size = ip->i_size;
ds.st_atime = ip->i_un.i_netatime;
ds.st_mtime = ip->i_un.i_netmtime;
ds.st_ctime = ip->i_un.i_netctime;
ds.st_rdev = ip->i_un.i_netrdev;
if(copyout((caddr_t)&ds, (caddr_t)ub, sizeof(ds)))
u.u_error = EFAULT;
ip->i_un.i_fsflags &=~ NSTOK; /* so repeated fstats check for changes */
}
nbread(ip)
register struct inode *ip;
{
unsigned char sbuf[SRDSIZE];
register unsigned char *rp;
register unsigned char *sp;
register int n;
ip->i_un.i_fsflags &=~ NSTOK;
rp = getnbbuf();
sp = sbuf;
bzero(sp, sizeof(sbuf));
tonetchar(sp, SNB_VERSION, NETB);
tonetchar(sp, SNB_CMD, NBREAD);
tonetlong(sp, SNB_LEN, sizeof(sbuf));
tonetlong(sp, SNB_TAG, ip->i_un.i_tag);
do {
n = u.u_count;
if(n > nbbsz - RNBSIZE)
n = nbbsz - RNBSIZE;
tonetlong(sp, SRD_LEN, n);
tonetlong(sp, SRD_OFFSET, Ltol(u.u_offset));
nbsend(ip, sp, rp, nbbsz);
if (u.u_error)
break;
n = frnetlong(rp, RNB_LEN) - RNBSIZE;
if(n > 0 && n <= nbbsz) /* give it to user */
iomove(rp + RNBSIZE, n, B_READ);
} while (u.u_error == 0 && u.u_count > 0 && n > 0
&& frnetchar(rp, RNB_FLAGS) != NBEND);
putnbbuf(rp);
}
nbdirread(ip, len)
register struct inode *ip;
{
unsigned char sbuf[SRDSIZE];
register unsigned char *sp;
register unsigned char *rp; /* RDISIZE */
int n;
ip->i_un.i_fsflags &=~ NSTOK;
rp = getnbbuf();
sp = sbuf;
bzero(sp, sizeof(sbuf));
tonetchar(sp, SNB_VERSION, NETB);
tonetchar(sp, SNB_CMD, NBDIR);
tonetlong(sp, SNB_LEN, sizeof(sbuf));
tonetlong(sp, SNB_TAG, ip->i_un.i_tag);
if(len > nbbsz - RDISIZE)
len = nbbsz - RDISIZE;
tonetlong(sp, SRD_LEN, len);
tonetlong(sp, SRD_OFFSET, Ltol(u.u_offset));
nbsend(ip, sp, rp, nbbsz);
if(u.u_error)
goto out;
n = frnetlong(rp, RNB_LEN) - RDISIZE;
if(n > 0) { /* why not iomove? */
if(u.u_segflg != SEGSYS) {
if(copyout(rp + RDISIZE, u.u_base, n))
u.u_error = EFAULT;
}
else
bcopy(rp + RDISIZE, u.u_base, n);
}
u.u_offset = Lladd(u.u_offset, frnetlong(rp, RDI_USED));
u.u_r.r_val1 = n;
out:
putnbbuf(rp);
}
nbwrite(ip)
register struct inode *ip;
{
register unsigned char *sp;
unsigned char rbuf[RNBSIZE];
int n;
ip->i_un.i_fsflags &=~ NSTOK;
sp = getnbbuf();
bzero(sp, SWRSIZE);
tonetchar(sp, SNB_VERSION, NETB);
tonetchar(sp, SNB_CMD, NBWRT);
tonetlong(sp, SNB_TAG, ip->i_un.i_tag);
do {
n = u.u_count;
if(n > nbbsz - SWRSIZE)
n = nbbsz - SWRSIZE;
tonetlong(sp, SNB_LEN, n + SWRSIZE);
tonetlong(sp, SWR_LEN, n);
tonetlong(sp, SWR_OFFSET, Ltol(u.u_offset));
iomove(sp + SWRSIZE, n, B_WRITE);
if (u.u_error)
break;
nbsend(ip, sp, rbuf, sizeof(rbuf));
if (u.u_error)
break;
ip->i_flag |= IUPD|ICHG;
} while(u.u_count > 0);
if(u.u_error == 0)
ip->i_size = frnetlong(rbuf, RNB_FSIZE);
putnbbuf(sp);
}
nbioctl(ip, cmd, addr, flag) /* fp->f_flag */
register struct inode *ip;
caddr_t addr;
{
register unsigned char *p;
int size;
switch(cmd) {
case NBIOCON:
nbdebug = 1;
return;
case NBIOCOFF:
nbdebug = 0;
return;
case NBIOCBSZ:
if (copyin(addr, (caddr_t)&size, sizeof(size))) {
u.u_error = EFAULT;
return;
}
if (size > 1024 && size <= NBMAXMSG)
nbbsz = size & ~1023; /* round to even 1K */
if (copyout(addr, (caddr_t)&nbbsz, sizeof(nbbsz)))
u.u_error = EFAULT;
return;
}
/*
* one buffer, used first for sbuf, then for rbuf
*/
p = getnbbuf();
bzero(p, SIOSIZE);
tonetchar(p, SNB_VERSION, NETB);
tonetchar(p, SNB_CMD, NBIOCTL);
tonetlong(p, SNB_LEN, SIOSIZE + SIODATA);
tonetlong(p, SNB_TAG, ip->i_un.i_tag);
tonetlong(p, SIO_CMD, cmd);
tonetshort(p, SIO_FLAG, flag);
if(copyin(addr, p + SIOSIZE, SIODATA)) {
u.u_error = EFAULT;
goto out;
}
nbsend(ip, p, p, RNBSIZE + SIODATA);
if (u.u_error)
goto out;
if(frnetlong(p, RNB_LEN) != RNBSIZE + SIODATA) {
printf("netb: nbioc got %d, not %d\n",
frnetlong(p, RNB_LEN), RNBSIZE+SIODATA);
u.u_error = EIO;
goto out;
}
if(copyout(p+RNBSIZE, addr, SIODATA))
u.u_error = EFAULT;
out:
putnbbuf(p);
}
/*
* namei:
* send the whole pathname
* wrong if another filesystem is mounted
* atop some file in this one
*/
nbnami(p, flagp, follow)
register struct nx *p;
register struct argnamei *flagp;
{
register char *cp;
register struct inode *dp;
register unsigned char *pp;
register int nlen;
int used;
dev_t dev;
dp = p->dp;
/*
* special case:
* if simple lookup of "" in the root,
* and the connection is dead,
* return OK even though dead so it's possible to unmount
*/
if (dp->i_mpoint->i_mroot == dp
&& dp->i_un.i_fsflags & NDEAD
&& flagp->flag == NI_SEARCH && *p->cp == 0)
return (0); /* with the same dp and cp */
/*
* one buffer for rbuf and sbuf
*/
pp = getnbbuf();
bzero(pp, SNMSIZE);
tonetchar(pp, SNB_VERSION, NETB);
tonetchar(pp, SNB_CMD, NBNAMI);
tonetlong(pp, SNB_TAG, dp->i_un.i_tag);
cp = p->cp;
while (*cp++)
;
nlen = cp - p->cp; /* length of name, including NUL */
if (nlen + SNMSIZE >= nbbsz) {
u.u_error = ENOENT;
goto outnull;
}
used = nlen + SNMSIZE - 1; /* temporary to improve code */
tonetlong(pp, SNB_LEN, used);
bcopy(p->cp, pp + SNMSIZE, nlen);
tonetchar(pp, SNB_FLAGS, flagp->flag);
switch (flagp->flag) {
case NI_CREAT:
case NI_NXCREAT:
case NI_MKDIR:
tonetshort(pp, SNM_MODE, flagp->un.mode);
break;
case NI_LINK:
tonetlong(pp, SNM_INO, flagp->un.il->i_un.i_tag);
break;
}
nbsend(dp, pp, pp, RNMSIZE);
if(u.u_error)
goto outnull;
if (frnetchar(pp, RNB_FLAGS) == NBROOT) {
/* find my root */
dp = dp->i_mpoint;
iput(p->dp);
if(dp == NULL) {
u.u_error = EGREG;
goto out;
}
plock(dp);
dp->i_count++;
used = frnetlong(pp, RNM_USED);
if (used < 2 || used >= nlen) {
u.u_error = EGREG;
goto outnull;
}
cp = p->cp + used;
*--cp == '.';
*--cp == '.';
p->cp = cp;
goto outmore;
}
switch(flagp->flag) {
default:
panic("nbnami");
case NI_CREAT: /* only case where flagp is used later (creat()) */
case NI_NXCREAT: /* return an inode, like search */
flagp->un.mode = ~flagp->un.mode; /* obscure side-effects! */
case NI_SEARCH:
break;
case NI_DEL: /* nothing returned */
case NI_LINK: /* nothing returned */
case NI_MKDIR: /* nothing returned */
case NI_RMDIR: /* nothing returned */
goto outnull;
}
/*
* release directory inode, construct a new one
* fill in the attributes even if inode was already in core;
* the new data is more correct (server gets priority)
*/
prele(dp); /* in case new == old */
/* cheat: first character of RNM_DEV == minor */
dev = makedev(major(dp->i_dev), frnetchar(pp, RNM_DEV));
dp = iget(dp, dev, frnetlong(pp, RNM_INO));
/* p->dp == dp before iget was called */
if (dp == NULL) {
idec(p->dp);
goto out;
}
if (p->dp->i_mpoint != dp->i_mpoint) { /* something mounted here */
idec(p->dp);
p->cp += nlen - 1; /* point to NUL at the end */
goto outmore;
}
dp->i_un.i_cip = p->dp->i_un.i_cip;
idec(p->dp);
nbunpack(dp, pp);
/*
* no filename to save for accounting; oh well
*/
if(flagp->flag == NI_SEARCH && flagp->un.buf)
bcopy("netb exec", flagp->un.buf, flagp->len);
/*
* return the inode in dp
*/
out:
p->dp = dp;
putnbbuf(pp);
return (0);
/*
* discard dp, return NULL
*/
outnull:
iput(dp);
p->dp = NULL;
putnbbuf(pp);
return (0);
/*
* crossed to a different filesystem;
* return cp and dp
*/
outmore:
p->dp = dp;
putnbbuf(pp);
return (1);
}
nbmount(cip, dip, flag, mnt, fstyp)
struct inode *cip, *dip;
{
nbfstyp = fstyp;
if (mnt)
nbon(cip, dip, flag, fstyp);
else
nboff(dip);
}
/*
* mount the filesystem
* fake up a namei message to fetch the root
* don't call nbnami because it's too hard to get
* all the first-time error checks right
*/
nbon(cip, dip, flag, fstyp)
struct inode *cip;
struct inode *dip;
{
register struct inode *rip;
struct inode pi;
register unsigned char *pp;
unsigned char sbuf[SNMSIZE+1];
unsigned char rbuf[RNMSIZE];
register dev_t dev;
if(cip->i_sptr == NULL) { /* shouting into the storm */
u.u_error = ENXIO;
return;
}
if (dip->i_fstyp == fstyp && major(dip->i_dev) == major(flag)) {
u.u_error = EBUSY;
return;
}
/*
* fake up an inode to represent the connection
* to nbsend and iget
*/
pi.i_fstyp = fstyp;
pi.i_mpoint = π
pi.i_mroot = π /* pi.i_mpoint->i_mroot == &pi */
pi.i_un.i_cip = cip;
pi.i_un.i_fsflags = 0;
pp = sbuf;
bzero(pp, sizeof(sbuf));
tonetchar(pp, SNB_VERSION, NETB);
tonetchar(pp, SNB_CMD, NBNAMI);
tonetlong(pp, SNB_TAG, NBROOTTAG); /* lookup in the root */
tonetlong(pp, SNB_LEN, SNMSIZE+1); /* one char of filename */
/* tonetchar(pp, SNMSIZE, 0); /* "" */
tonetchar(pp, SNB_FLAGS, NI_SEARCH);
nbsend(&pi, pp, rbuf, sizeof(rbuf));
if (u.u_error)
return;
pp = rbuf;
if (frnetchar(pp, RNB_FLAGS) != 0) {
u.u_error = EINVAL;
return;
}
dev = makedev(major(flag), frnetchar(pp, RNM_DEV));
if ((rip = iget(&pi, dev, frnetlong(pp, RNM_INO))) == NULL)
return;
if (rip->i_count != 1) {
iput(rip);
u.u_error = EBUSY;
return;
}
nbunpack(rip, pp);
rip->i_un.i_cip = cip;
dip->i_mroot = rip;
rip->i_mpoint = dip;
prele(rip);
dip->i_count++;
cip->i_count++;
}
/*
* unpack inode info from namei response
* a pity stat can't use this too,
* but the stat response isn't quite the same format
*/
nbunpack(dp, pp)
register struct inode *dp;
register unsigned char *pp;
{
dp->i_un.i_tag = frnetlong(pp, RNM_TAG);
dp->i_mode = frnetshort(pp, RNM_MODE);
dp->i_nlink = frnetshort(pp, RNM_NLINK);
dp->i_uid = frnetshort(pp, RNM_UID);
dp->i_gid = frnetshort(pp, RNM_GID);
dp->i_size = frnetlong(pp, RNM_SIZE);
dp->i_un.i_netatime = frnetlong(pp, RNM_ATIME);
dp->i_un.i_netmtime = frnetlong(pp, RNM_MTIME);
dp->i_un.i_netctime = frnetlong(pp, RNM_CTIME);
dp->i_un.i_netrdev = frnetlong(pp, RNM_RDEV);
if (dp->i_count == 1)
dp->i_un.i_fsflags = 0;
dp->i_un.i_fsflags |= NSTOK;
}
/*
* unmount a netb--
* called from above, or from nbsend when there's an error
* mip is the mount point
* locking needs some thought
*/
nboff(mip)
register struct inode *mip;
{
register struct inode *rip;
rip = mip->i_mroot;
plock(rip);
if (rip->i_mpoint != mip) { /* already unmounted */
prele(rip);
return;
}
xumount(rip);
iupdat(rip, &time, &time, 1);
ifsinacc(rip); /* turn all, including rip, to errfs */
plock(mip);
mip->i_mroot = NULL;
iput(mip);
if (rip->i_un.i_cip == NULL)
panic("nboff");
iput(rip->i_un.i_cip);
iput(rip); /* errput, not nbput */
}
nbsend(ip, sp, rp, size)
struct inode *ip;
register unsigned char *sp;
unsigned char *rp;
{
int bad;
long tn;
struct inode *cip;
register struct inode *rip;
cip = ip->i_un.i_cip;
rip = ip->i_mpoint->i_mroot;
if (rip == NULL || rip->i_fstyp != ip->i_fstyp) {
u.u_error = EIO;
return;
}
while ((rip->i_un.i_fsflags & (NBUSY|NDEAD)) == NBUSY
&& ip->i_fstyp == nbfstyp) {
rip->i_un.i_fsflags |= NWANT;
if (tsleep((caddr_t)cip, PZERO+1, 0) != TS_OK) {
u.u_error = EIO;
return;
}
}
if (ip->i_fstyp != nbfstyp /* unmounted under us */
|| rip->i_un.i_fsflags & NDEAD) { /* or connection fell down */
u.u_error = EIO;
return;
}
rip->i_un.i_fsflags |= NBUSY;
tonetshort(sp, SNB_UID, u.u_uid);
tonetshort(sp, SNB_GID, u.u_gid);
tn = nbtrannum++;
tonetlong(sp, SNB_TRANNUM, tn);
if (istwrite(cip, sp, frnetlong(sp, SNB_LEN)) >= 0
&& nbrecv(cip, rp, size, tn) >= 0)
bad = 0;
else
bad = 1;
if (ip->i_fstyp == nbfstyp) { /* in case unmounted while asleep */
rip->i_un.i_fsflags &=~ NBUSY;
if (rip->i_un.i_fsflags & NWANT) {
wakeup((caddr_t)cip);
rip->i_un.i_fsflags &=~ NWANT;
}
if (bad)
rip->i_un.i_fsflags |= NDEAD;
}
if (bad)
u.u_error = EIO;
else if ((bad = frnetshort(rp, RNB_ERRNO)) != 0)
u.u_error = bad;
}
/*
* read an answer,
* and make sure it smells like one of ours
*/
int
nbrecv(cip, rp, size, tn)
struct inode *cip;
register unsigned char *rp;
int size;
long tn;
{
unsigned char *p;
register int n, len, netlen;
loop:
p = rp;
len = 0;
/*
* header
*/
while (len < RNBSIZE) {
n = istread(cip, p, size - len, 0);
if (n <= 0)
return (-1);
len += n;
p += n;
}
netlen = frnetlong(rp, RNB_LEN);
if (netlen > size || netlen < RNBSIZE) {
printf("netb: rcv len %d\n", netlen);
return (-1);
}
/*
* following data, if any
*/
while (len < netlen) {
n = istread(cip, p, size - len, 0);
if (n <= 0)
return (-1);
len += n;
p += n;
}
if (frnetlong(rp, RNB_TRANNUM) != tn) {
if (frnetlong(rp, RNB_TRANNUM) < tn) /* echo from the past */
goto loop;
printf("netb: exp tran %d, got %d\n", tn, frnetlong(rp, RNB_TRANNUM));
return (-1);
}
return (len);
}
/* oh no, it's got its own tiny buffer pool: want to avoid breakage on
normal (4k reads for now). we could replace this with a real
memory manager in the kernel */
#define NBUF 7 /* who knows? */
char nb_lock;
char nbflgs[NBUF];
unsigned char nbbuf[NBUF][NBMAXMSG];
unsigned char *
getnbbuf()
{
register int i;
loop:
for(i = 0; i < NBUF; i++)
if(!nbflgs[i]) {
nbflgs[i] = 1;
return(nbbuf[i]);
}
nb_lock = 1;
sleep((caddr_t)&nb_lock, PZERO);
goto loop;
}
putnbbuf(s)
unsigned char *s;
{
register int i;
for(i = 0; i < NBUF; i++)
if(nbbuf[i] == s) {
nbflgs[i] = 0;
if(nb_lock) {
nb_lock = 0;
wakeup((caddr_t) &nb_lock);
}
return;
}
panic("putnbbuf");
}