/* new version for netfs */ #include "../h/param.h" #include "../h/dir.h" #include "../h/user.h" #include "../h/netb.h" #include "../h/inode.h" #include "../h/buf.h" #include "../h/systm.h" #include "../h/stat.h" #include "../h/mount.h" #include "../h/file.h" extern long trannum; struct sendb nilsendb = {NETB}; char *getnbbuf(); #define BSZ (5*1024) /* probably enough */ /* cache to avoid stat messages immediately after nami. The cache is not * invalidated by iput (unless it is the cached item) because nami iput's * the directory before returning. The cache is critical for the iget * inside nami. Nbnami fills nbcache for iget to read, and there is a * race condition since nami does iput before the iget. */ struct nbcache { int valid; dev_t dev; ino_t ino; /* although we get long back from server */ long tag; short mode; short nlink; short uid, gid; dev_t rdev; /* does anyone care? */ long size; time_t ta, tm, tc; struct inode *cip; /* the communications handle */ } nbcache; /* called indirectly through iget on behalf of nami */ /* or else it is called for the root inode */ struct inode * nbget(fip, dev, ino, ip) struct inode *fip; dev_t dev; ino_t ino; register struct inode *ip; { if(minor(dev) == 0 && ino == ROOTINO) { /* construct root */ ip->i_un.i_cip = fip->i_un.i_cip; ip->i_dev = fip->i_dev; ip->i_un.i_tag = (ip->i_dev<<16)|ROOTINO; /* server better understand */ ip->i_mode = S_IFDIR | 0555; /* perms corrected by stat */ ip->i_uid = 1; /* ditto */ ip->i_gid = 1; /* ditto */ ip->i_nlink = 2; /* ditto */ ip->i_size = 512; /* ditto */ return(ip); } if(nbcache.valid == 0) { printf("nbget: nbcache invalid\n"); u.u_error = ENOENT; return(0); } if(nbcache.dev != dev) { printf("nbget: dev invalid 0x%x 0x%x\n", nbcache.dev, dev); u.u_error = ENOENT; return(0); } /* in this error, should the ino be freed? */ if(nbcache.ino != ino) { printf("nbget: ino invalid 0x%x 0x%x\n", nbcache.ino, ino); u.u_error = ENOENT; return(0); } ip->i_un.i_cip = nbcache.cip; ip->i_dev = dev; ip->i_mode = nbcache.mode; ip->i_un.i_tag = nbcache.tag; ip->i_uid = nbcache.uid; ip->i_gid = nbcache.gid; ip->i_nlink = nbcache.nlink; ip->i_size = nbcache.size; return(ip); } nbfree() { /* that's easy (after free, cache can't be looked at for that inode without another nami) */ } nbput(ip) struct inode *ip; { struct sendb x; struct recvb y; x = nilsendb; /* should check to see if nbcache is being tossed */ /* don't put root, as optimization */ if(ip->i_number == ROOTINO && minor(ip->i_dev) == 0) return; x.cmd = NBPUT; x.len = sizeof(struct sendb); x.tag = ip->i_un.i_tag; nbsend(ip, &x, &y, sizeof(y)); } nbtrunc(ip) struct inode *ip; { struct sendb x; struct recvb y; /* we might be truncing the cached inode */ x = nilsendb; x.cmd = NBTRNC; x.len = sizeof(struct sendb); x.tag = ip->i_un.i_tag; nbsend(ip, &x, &y, sizeof(y)); } /* the only times we're interested in are those which have waitfor == 0 * so fix it! (or those which have ta or tb != &time) */ nbupdat(ip, ta, tb, waitfor) time_t *ta, *tb; struct inode *ip; { struct { struct sendb x; struct snbup b; } a; struct recvb y; /* what about the cache? */ a.x = nilsendb; a.x.cmd = NBUPD; a.x.len = sizeof(a); a.x.tag = ip->i_un.i_tag; a.b.uid = ip->i_uid; a.b.gid = ip->i_gid; a.b.mode = ip->i_mode; if((ta != &time) && (ip->i_flag & IACC)) a.b.ta = *ta; else a.b.ta = 0; if((tb != &time) && (ip->i_flag & IUPD)) a.b.tm = *tb; else a.b.tm = 0; nbsend(ip, (struct sendb *) &a, &y,sizeof(y)); ip->i_flag &= ~(IUPD|IACC|ICHG); } nbstat(ip, ub) struct inode *ip; struct stat *ub; { struct { struct sendb x; struct snbstat s; } a; struct { struct recvb y; struct rnbstat s; } b; struct stat ds; if(nbcache.valid && /* always, except for fstat() and root */ nbcache.cip == ip->i_un.i_cip && nbcache.tag == ip->i_un.i_tag) { /* this first batch should already be in the inode */ ds.st_dev = nbcache.dev; ds.st_ino = nbcache.ino; ds.st_mode = nbcache.mode; ds.st_nlink = nbcache.nlink; ds.st_uid = nbcache.uid; ds.st_gid = nbcache.gid; /* new stuff */ ds.st_size = nbcache.size; ds.st_atime = nbcache.ta; ds.st_mtime = nbcache.tm; ds.st_ctime = nbcache.tc; goto outit; } a.x = nilsendb; a.x.trannum = trannum++; a.x.cmd = NBSTAT; a.x.len = sizeof(a); a.x.tag = ip->i_un.i_tag; a.s.ta = time; nbsend(ip, (struct sendb *)&a, (struct recvb *)&b, sizeof(b)); if(u.u_error) return; /* unpack the returned stuff */ ds.st_dev = ip->i_dev; ds.st_ino = ip->i_number; ds.st_mode = ip->i_mode = b.s.mode; ds.st_nlink = ip->i_nlink = b.s.nlink; ds.st_uid = ip->i_uid = b.s.uid; ds.st_gid = ip->i_gid = b.s.gid; ds.st_size = (ip->i_size = b.s.size); ds.st_atime = b.s.ta; ds.st_mtime = b.s.tm; ds.st_ctime = b.s.tc; outit: if(copyout((caddr_t)&ds, (caddr_t)ub, sizeof(ds))) u.u_error = EFAULT; } nbread(ip) struct inode *ip; { struct { struct sendb x; struct snbread r; } a; struct recvb *y; char *bp; int n; /* if this is the cached item, some time is now wrong */ bp = getnbbuf(); a.x = nilsendb; a.x.cmd = NBREAD; a.x.tag = ip->i_un.i_tag; a.x.len = sizeof(a); do { n = u.u_count; if(n > BSZ - sizeof(*y)) n = BSZ - sizeof(*y); a.r.offset = u.u_offset; a.r.len = n; nbsend(ip, (struct sendb *)&a, (struct recvb *)bp, sizeof(*y)); y = (struct recvb *)(bp); n = y->len - sizeof(*y); if(n > 0) { /* give it to user */ if(u.u_segflg != 1) { if(copyout(bp + sizeof(*y), u.u_base, n)) { u.u_error = EFAULT; break; } } else bcopy(bp + sizeof(*y), u.u_base, n); u.u_base += n; u.u_offset += n; u.u_count -= n; } if(y->errno) u.u_error = y->errno; } while(u.u_error == 0 && u.u_count > 0 && n > 0); putnbbuf(bp); } nbwrite(ip) struct inode *ip; { struct xx { struct sendb x; struct snbwrite w; } *a; struct recvb y; int n; char *bp; /* if this is the cached item, it's not quite right any more*/ bp = getnbbuf(); a = (struct xx *)(bp); a->x = nilsendb; a->x.cmd = NBWRT; a->x.tag = ip->i_un.i_tag; do { n = u.u_count; if(n > BSZ - sizeof(*a)) n = BSZ - sizeof(*a); a->x.len = n + sizeof(*a); a->w.len = n; a->w.offset = u.u_offset; if(u.u_segflg != 1) { if(copyin(u.u_base, bp + sizeof(*a), n)) { u.u_error = EFAULT; break; } } else bcopy(u.u_base, bp + sizeof(*a), n); nbsend(ip, (struct sendb *)a, &y, sizeof(y)); if(y.errno) { u.u_error = y.errno; break; } ip->i_flag |= IUPD|ICHG; u.u_count -= n; u.u_offset += n; u.u_base += n; } while(u.u_error == 0 && u.u_count > 0); putnbbuf(bp); } /* send the whole path over. this fails if another file system is mounted * on this one, or if someone has chrooted into this one */ nbnami(p, pflagp, follow) struct nx *p; struct argnamei **pflagp; { char *bp, *s; struct inode *dp; struct aa { struct sendb x; struct snbnami n; } *a; struct bb { struct recvb y; struct rnbnami n; } *b; register int i; for(i = 0, s = p->cp; *s; s++) i++; if(i + sizeof(*a) >= BSZ || i + sizeof(*b) >= BSZ) { u.u_error = ENOENT; goto outnull; } /*printf("nbnami %s\n", p->cp);*/ dp = p->dp; bp = getnbbuf(); a = (struct aa *)(bp); a->x = nilsendb; a->x.cmd = NBNAMI; a->x.len = i + sizeof(*a); a->x.tag = dp->i_un.i_tag; a->n.uid = u.u_uid; a->n.gid = u.u_gid; if(*pflagp) { a->x.flags = (*pflagp)->flag; a->n.mode = (*pflagp)->mode; if((*pflagp)->il) a->n.ino = (*pflagp)->il->i_un.i_tag; /* fix the struct? */ else a->n.ino = 0; } for(s = bp + sizeof(*a), i = 0; p->cp[i]; i++) *s++ = p->cp[i]; *s = 0; b = (struct bb *) (bp); nbsend(dp, (struct sendb *)a, (struct recvb *)b, sizeof(*b)); if(u.u_error) goto outnull; if(b->y.flags == NBROOT) { /* find my root */ dp = dp->i_mpoint; iput(p->dp); plock(dp); dp->i_count++; if(!dp) { printf("nbnami lost root %s\n", p->cp); u.u_error = EGREG; goto outnull; } /*printf("NBROOT: used %d %s dp #%x ino %d fstyp %d\n", b->n.used, p->cp, dp, dp->i_number, dp->i_fstyp);*/ p->cp += b->n.used; /* now we have to prepend the .. */ if(b->n.used < 2) { u.u_error = EGREG; goto outnull; } *--p->cp == '.'; *--p->cp == '.'; /*printf("and %s\n", p->cp);*/ goto more; } if(*pflagp == 0) goto saveit; switch((*pflagp)->flag) { default: printf("nbnami switch %c\n", (*pflagp)->flag); goto outnull; case NI_CREAT: /* only case where pflagp is used later (creat()) */ case NI_NXCREAT: /* return an inode, like search */ (*pflagp)->done = b->n.ino; /* fix the struct? */ goto saveit; case NI_DEL: /* nothing returned */ case NI_LINK: /* nothing returned */ case NI_MKDIR: /* returns nothing */ case NI_RMDIR: /* nothing returned */ goto outnull; } saveit: /* release p->dp, construct a locked inode, and cache for stat */ /* first the cache (should there be more checking?) */ nbcache.valid = 1; nbcache.tag = b->n.tag; nbcache.ino = b->n.ino; nbcache.dev = b->n.dev; nbcache.mode = b->n.mode; nbcache.nlink = b->n.nlink; nbcache.uid = b->n.uid; nbcache.gid = b->n.gid; nbcache.rdev = b->n.rdev; nbcache.size = b->n.size; nbcache.ta = b->n.ta; nbcache.tm = b->n.tm; nbcache.tc = b->n.tc; nbcache.cip = p->dp->i_un.i_cip; /* if that was the root, uncache it, for otherwise a lost conenction * would go undetected. */ if(minor(nbcache.dev) == 0 && nbcache.ino == ROOTINO) nbcache.valid = 0; /* release old, construct new */ prele(p->dp); dp = iget(p->dp, b->n.dev, b->n.ino); iput(p->dp); goto outgood; outnull: /* nami is not going to return an inode */ p->dp = dp; putnbbuf(bp); return(1); outgood: p->dp = dp; putnbbuf(bp); return(0); more: /* popped out, try some more */ p->dp = dp; putnbbuf(bp); return(2); } nbmount(cip, dip, flag, mnt, fstyp) struct inode *cip, *dip; { if (mnt) nbon(cip, dip, flag, fstyp); else nboff(dip, fstyp); } nbon(cip, dip, flag, fstyp) register struct inode *cip, *dip; { struct inode pi; register struct inode *rip; if(cip->i_sptr == NULL) { /* shouting into the storm */ u.u_error = ENXIO; return; } if((dip->i_mode & IFMT) != IFDIR) { u.u_error = ENOTDIR; return; } pi.i_dev = flag; pi.i_fstyp = fstyp; pi.i_un.i_cip = cip; if ((rip = iget(&pi, flag, ROOTINO)) == NULL) return; if (rip->i_count > 1) { iput(rip); u.u_error = EBUSY; return; } dip->i_mroot = rip; rip->i_mpoint = dip; rip->i_un.i_cip = cip; prele(rip); dip->i_count++; dip->i_flag |= IMOUNT; cip->i_count++; cip->i_un.i_key = 0; } #define ERRFS 5 /* mip should be the guy mounted on */ nboff(mip, fstyp) register struct inode *mip; { register struct inode *xip, *rip; dev_t dev; rip = mip->i_mroot; if(!rip) panic("!rip in nboff\n"); if(rip->i_fstyp != fstyp) panic("netb, wrong fstyp"); plock(rip); dev = rip->i_dev; xumount(dev); /* remove sticky texts (independent of fstyp) */ /* scan all the inodes, looking for us */ for(xip = inode; xip < inodeNINODE; xip++) { if (xip == rip) continue; if(xip->i_number == 0 || xip->i_fstyp != fstyp || major(xip->i_dev) != major(dev)) continue; xumount(xip->i_dev); /* does this ever help? */ /* let's use the error fs, and hope ip gets put someday */ xip->i_fstyp = ERRFS; } if(u.u_error) { prele(rip); return; } mip->i_flag &= ~IMOUNT; mip->i_mroot = NULL; iput(mip); if (rip->i_un.i_cip == NULL) panic("naunmount2"); iput(rip); stclose(rip->i_un.i_cip, 1); /* does that iput it too? */ } nbsend(ip, s, r, hdrsize) struct inode *ip; struct sendb *s; struct recvb *r; { int n, large, tn, cnt; char *p; struct inode *cip = ip->i_un.i_cip; /* use key as lock */ while(ip->i_fstyp != ERRFS && cip->i_un.i_key) sleep((caddr_t)cip, PZERO); if(ip->i_fstyp == ERRFS) { /* it was unmounted under us */ u.u_error = EIO; return; } cip->i_un.i_key = 1; u.u_error = 0; tn = s->trannum = trannum++; n = istwrite(cip, (char *)s, s->len); if(n < 0) { r->errno = EIO; goto rotten; } switch(s->cmd) { default: printf("nbsend cmd %d\n", s->cmd); goto rotten; case NBSTAT: case NBTRNC: case NBUPD: case NBPUT: case NBWRT: case NBNAMI: large = hdrsize; break; case NBREAD: large = BSZ; } readagain: p = (char *)r; cnt = 0; readmore: n = istread(cip, p, large); if(n <= 0) goto dying; cnt += n; p += n; if(cnt < sizeof(struct recvb)) goto readmore; if(cnt < r->len) { if(!r->errno) goto readmore; dying: if(!r->errno) r->errno = EIO; if(n >= 0) istwrite(cip, s, 0); /* shut it off */ goto rotten; } if(cnt != r->len) { /* could we get too much? */ printf("netb read %d, was sent %d\n", cnt, r->len); goto dying; } if(tn != r->trannum) { printf("netb got tran %d for %d\n", tn, r->trannum); if(r->trannum < tn) goto readagain; /* distant past? */ istwrite(cip, s, 0); /* crunch */ goto rotten; } if(cip->i_un.i_key) { cip->i_un.i_key = 0; wakeup((caddr_t)cip); } if(r->errno) u.u_error = r->errno; return; rotten: cip->i_un.i_key = 0; wakeup((caddr_t)cip); if(r->errno) u.u_error = r->errno; else u.u_error = EIO; /* and we could unmount the file system here too, if we could find the root */ } /* 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 5 /* who knows? */ int nb_lock; int nbflgs[NBUF]; char nbbuf[NBUF][BSZ]; char * getnbbuf() { 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) char *s; { 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; } /* this is a bad deal */ printf("putnbbuf: s 0x%x BSZ 0x%x nbbuf 0x%x\n", s, BSZ, nbbuf); }