mirror of
https://github.com/0intro/conterm
synced 2024-11-25 23:19:36 +03:00
822 lines
13 KiB
C
822 lines
13 KiB
C
#include "u.h"
|
|
#include "lib.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "error.h"
|
|
|
|
typedef struct Fid Fid;
|
|
typedef struct Export Export;
|
|
typedef struct Exq Exq;
|
|
|
|
#define nil ((void*)0)
|
|
|
|
enum
|
|
{
|
|
Nfidhash = 1,
|
|
MAXRPC = MAXMSG+MAXFDATA,
|
|
MAXDIRREAD = (MAXFDATA/DIRLEN)*DIRLEN
|
|
};
|
|
|
|
struct Export
|
|
{
|
|
Ref r;
|
|
Exq* work;
|
|
Lock fidlock;
|
|
Fid* fid[Nfidhash];
|
|
Chan* root;
|
|
Chan* io;
|
|
Pgrp* pgrp;
|
|
int npart;
|
|
char part[MAXRPC];
|
|
};
|
|
|
|
struct Fid
|
|
{
|
|
Fid* next;
|
|
Fid** last;
|
|
Chan* chan;
|
|
long offset;
|
|
int fid;
|
|
int ref; /* fcalls using the fid; locked by Export.Lock */
|
|
int attached; /* fid attached or cloned but not clunked */
|
|
};
|
|
|
|
struct Exq
|
|
{
|
|
Lock lk;
|
|
int nointr;
|
|
int noresponse; /* don't respond to this one */
|
|
Exq* next;
|
|
int shut; /* has been noted for shutdown */
|
|
Export* export;
|
|
void* slave;
|
|
Fcall rpc;
|
|
char buf[MAXRPC];
|
|
};
|
|
|
|
struct
|
|
{
|
|
Lock l;
|
|
Qlock qwait;
|
|
Rendez rwait;
|
|
Exq *head; /* work waiting for a slave */
|
|
Exq *tail;
|
|
}exq;
|
|
|
|
static void exshutdown(Export*);
|
|
static void exflush(Export*, int, int);
|
|
static void exslave(void*);
|
|
static void exfree(Export*);
|
|
static void exportproc(Export*);
|
|
|
|
static char* Exauth(Export*, Fcall*);
|
|
static char* Exattach(Export*, Fcall*);
|
|
static char* Exclunk(Export*, Fcall*);
|
|
static char* Excreate(Export*, Fcall*);
|
|
static char* Exopen(Export*, Fcall*);
|
|
static char* Exread(Export*, Fcall*);
|
|
static char* Exremove(Export*, Fcall*);
|
|
static char* Exstat(Export*, Fcall*);
|
|
static char* Exwalk(Export*, Fcall*);
|
|
static char* Exwrite(Export*, Fcall*);
|
|
static char* Exwstat(Export*, Fcall*);
|
|
static char* Exversion(Export*, Fcall*);
|
|
|
|
static char *(*fcalls[Tmax])(Export*, Fcall*);
|
|
|
|
static char Enofid[] = "no such fid";
|
|
static char Eseekdir[] = "can't seek on a directory";
|
|
static char Ereaddir[] = "unaligned read of a directory";
|
|
static int exdebug = 0;
|
|
|
|
int
|
|
sysexport(int fd)
|
|
{
|
|
Chan *c;
|
|
Export *fs;
|
|
|
|
if(waserror())
|
|
return -1;
|
|
|
|
c = fdtochan(fd, ORDWR, 1, 1);
|
|
poperror();
|
|
c->flag |= CMSG;
|
|
|
|
fs = mallocz(sizeof(Export));
|
|
fs->r.ref = 1;
|
|
fs->pgrp = up->pgrp;
|
|
refinc(&fs->pgrp->r);
|
|
refinc(&up->slash->r);
|
|
fs->root = up->slash;
|
|
refinc(&fs->root->r);
|
|
fs->root = domount(fs->root);
|
|
fs->io = c;
|
|
|
|
exportproc(fs);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
exportinit(void)
|
|
{
|
|
lock(&exq.l);
|
|
if(fcalls[Tversion] != nil) {
|
|
unlock(&exq.l);
|
|
return;
|
|
}
|
|
|
|
fmtinstall('F', fcallfmt);
|
|
fmtinstall('D', dirfmt);
|
|
fmtinstall('M', dirmodefmt);
|
|
fcalls[Tversion] = Exversion;
|
|
fcalls[Tauth] = Exauth;
|
|
fcalls[Tattach] = Exattach;
|
|
fcalls[Twalk] = Exwalk;
|
|
fcalls[Topen] = Exopen;
|
|
fcalls[Tcreate] = Excreate;
|
|
fcalls[Tread] = Exread;
|
|
fcalls[Twrite] = Exwrite;
|
|
fcalls[Tclunk] = Exclunk;
|
|
fcalls[Tremove] = Exremove;
|
|
fcalls[Tstat] = Exstat;
|
|
fcalls[Twstat] = Exwstat;
|
|
unlock(&exq.l);
|
|
}
|
|
|
|
void
|
|
exportproc(Export *fs)
|
|
{
|
|
Exq *q;
|
|
char *buf;
|
|
int n, cn, len;
|
|
|
|
exportinit();
|
|
|
|
for(;;){
|
|
q = mallocz(sizeof(Exq));
|
|
if(q == 0)
|
|
panic("no memory");
|
|
|
|
q->rpc.data = q->buf + MAXMSG;
|
|
|
|
buf = q->buf;
|
|
len = MAXRPC;
|
|
if(fs->npart) {
|
|
memmove(buf, fs->part, fs->npart);
|
|
buf += fs->npart;
|
|
len -= fs->npart;
|
|
goto chk;
|
|
}
|
|
for(;;) {
|
|
if(waserror())
|
|
goto bad;
|
|
|
|
n = (*devtab[fs->io->type].read)(fs->io, buf, len, 0);
|
|
poperror();
|
|
|
|
if(n <= 0)
|
|
goto bad;
|
|
|
|
buf += n;
|
|
len -= n;
|
|
chk:
|
|
n = buf - q->buf;
|
|
|
|
/* convM2S returns size of correctly decoded message */
|
|
cn = convM2S(q->buf, &q->rpc, n);
|
|
if(cn < 0){
|
|
iprint("bad message type in devmnt\n");
|
|
goto bad;
|
|
}
|
|
if(cn > 0) {
|
|
n -= cn;
|
|
if(n < 0){
|
|
iprint("negative size in devmnt");
|
|
goto bad;
|
|
}
|
|
fs->npart = n;
|
|
if(n != 0)
|
|
memmove(fs->part, q->buf+cn, n);
|
|
break;
|
|
}
|
|
}
|
|
if(exdebug)
|
|
iprint("export %d <- %F\n", getpid(), &q->rpc);
|
|
|
|
if(q->rpc.type == Tflush){
|
|
exflush(fs, q->rpc.tag, q->rpc.oldtag);
|
|
free(q);
|
|
continue;
|
|
}
|
|
|
|
q->export = fs;
|
|
refinc(&fs->r);
|
|
|
|
lock(&exq.l);
|
|
if(exq.head == nil)
|
|
exq.head = q;
|
|
else
|
|
exq.tail->next = q;
|
|
q->next = nil;
|
|
exq.tail = q;
|
|
unlock(&exq.l);
|
|
if(exq.qwait.first == nil) {
|
|
n = thread("exportfs", exslave, nil);
|
|
/* iprint("launch export (pid=%ux)\n", n); */
|
|
}
|
|
rendwakeup(&exq.rwait);
|
|
}
|
|
bad:
|
|
free(q);
|
|
exshutdown(fs);
|
|
exfree(fs);
|
|
}
|
|
|
|
void
|
|
exflush(Export *fs, int flushtag, int tag)
|
|
{
|
|
Exq *q, **last;
|
|
int n;
|
|
Fcall fc;
|
|
char buf[MAXMSG];
|
|
|
|
/* hasn't been started? */
|
|
lock(&exq.l);
|
|
last = &exq.head;
|
|
for(q = exq.head; q != nil; q = q->next){
|
|
if(q->export == fs && q->rpc.tag == tag){
|
|
*last = q->next;
|
|
unlock(&exq.l);
|
|
exfree(fs);
|
|
free(q);
|
|
goto Respond;
|
|
}
|
|
last = &q->next;
|
|
}
|
|
unlock(&exq.l);
|
|
|
|
/* in progress? */
|
|
lock(&fs->r.l);
|
|
for(q = fs->work; q != nil; q = q->next){
|
|
if(q->rpc.tag == tag && !q->noresponse){
|
|
lock(&q->lk);
|
|
q->noresponse = 1;
|
|
if(!q->nointr)
|
|
intr(q->slave);
|
|
unlock(&q->lk);
|
|
unlock(&fs->r.l);
|
|
goto Respond;
|
|
return;
|
|
}
|
|
}
|
|
unlock(&fs->r.l);
|
|
|
|
if(exdebug) iprint("exflush: did not find rpc: %d\n", tag);
|
|
|
|
Respond:
|
|
fc.type = Rflush;
|
|
fc.tag = flushtag;
|
|
n = convS2M(&fc, buf);
|
|
if(exdebug) iprint("exflush -> %F\n", &fc);
|
|
if(!waserror()){
|
|
(*devtab[fs->io->type].write)(fs->io, buf, n, 0);
|
|
poperror();
|
|
}
|
|
}
|
|
|
|
void
|
|
exshutdown(Export *fs)
|
|
{
|
|
Exq *q, **last;
|
|
|
|
lock(&exq.l);
|
|
last = &exq.head;
|
|
for(q = exq.head; q != nil; q = *last){
|
|
if(q->export == fs){
|
|
*last = q->next;
|
|
exfree(fs);
|
|
free(q);
|
|
continue;
|
|
}
|
|
last = &q->next;
|
|
}
|
|
unlock(&exq.l);
|
|
|
|
lock(&fs->r.l);
|
|
q = fs->work;
|
|
while(q != nil){
|
|
if(q->shut){
|
|
q = q->next;
|
|
continue;
|
|
}
|
|
q->shut = 1;
|
|
unlock(&fs->r.l);
|
|
/* postnote(q->slave, 1, "interrupted", NUser); */
|
|
iprint("postnote 2!\n");
|
|
lock(&fs->r.l);
|
|
q = fs->work;
|
|
}
|
|
unlock(&fs->r.l);
|
|
}
|
|
|
|
void
|
|
exfree(Export *fs)
|
|
{
|
|
Fid *f, *n;
|
|
int i;
|
|
|
|
if(refdec(&fs->r) != 0)
|
|
return;
|
|
closepgrp(fs->pgrp);
|
|
cclose(fs->root);
|
|
cclose(fs->io);
|
|
for(i = 0; i < Nfidhash; i++){
|
|
for(f = fs->fid[i]; f != nil; f = n){
|
|
if(f->chan != nil)
|
|
cclose(f->chan);
|
|
n = f->next;
|
|
free(f);
|
|
}
|
|
}
|
|
free(fs);
|
|
}
|
|
|
|
int
|
|
exwork(void *a)
|
|
{
|
|
return exq.head != nil;
|
|
}
|
|
|
|
void
|
|
exslave(void *a)
|
|
{
|
|
Export *fs;
|
|
Exq *q, *t, **last;
|
|
char *err;
|
|
int n;
|
|
/*
|
|
closepgrp(up->pgrp);
|
|
up->pgrp = nil;
|
|
*/
|
|
for(;;){
|
|
qlock(&exq.qwait);
|
|
rendsleep(&exq.rwait, exwork, nil);
|
|
|
|
lock(&exq.l);
|
|
q = exq.head;
|
|
if(q == nil) {
|
|
unlock(&exq.l);
|
|
qunlock(&exq.qwait);
|
|
continue;
|
|
}
|
|
exq.head = q->next;
|
|
q->slave = curthread();
|
|
unlock(&exq.l);
|
|
|
|
qunlock(&exq.qwait);
|
|
|
|
q->noresponse = 0;
|
|
q->nointr = 0;
|
|
fs = q->export;
|
|
lock(&fs->r.l);
|
|
q->next = fs->work;
|
|
fs->work = q;
|
|
unlock(&fs->r.l);
|
|
|
|
up->pgrp = q->export->pgrp;
|
|
|
|
if(exdebug > 1)
|
|
iprint("exslave dispatch %d %F\n", getpid(), &q->rpc);
|
|
|
|
if(waserror()){
|
|
iprint("exslave err %r\n");
|
|
err = up->errstr;
|
|
goto Err;
|
|
}
|
|
if(q->rpc.type >= Tmax || !fcalls[q->rpc.type])
|
|
err = "bad fcall type";
|
|
else
|
|
err = (*fcalls[q->rpc.type])(fs, &q->rpc);
|
|
|
|
poperror();
|
|
Err:;
|
|
q->rpc.type++;
|
|
if(err){
|
|
q->rpc.type = Rerror;
|
|
strncpy(q->rpc.ename, err, ERRLEN);
|
|
}
|
|
n = convS2M(&q->rpc, q->buf);
|
|
|
|
if(exdebug)
|
|
iprint("exslve %d -> %F\n", getpid(), &q->rpc);
|
|
|
|
lock(&q->lk);
|
|
if(q->noresponse == 0){
|
|
q->nointr = 1;
|
|
clearintr();
|
|
if(!waserror()){
|
|
(*devtab[fs->io->type].write)(fs->io, q->buf, n, 0);
|
|
poperror();
|
|
}
|
|
}
|
|
unlock(&q->lk);
|
|
|
|
/*
|
|
* exflush might set noresponse at this point, but
|
|
* setting noresponse means don't send a response now;
|
|
* it's okay that we sent a response already.
|
|
*/
|
|
if(exdebug > 1)
|
|
iprint("exslave %d written %d\n", getpid(), q->rpc.tag);
|
|
|
|
lock(&fs->r.l);
|
|
last = &fs->work;
|
|
for(t = fs->work; t != nil; t = t->next){
|
|
if(t == q){
|
|
*last = q->next;
|
|
break;
|
|
}
|
|
last = &t->next;
|
|
}
|
|
unlock(&fs->r.l);
|
|
|
|
exfree(q->export);
|
|
free(q);
|
|
}
|
|
iprint("exslave shut down");
|
|
threadexit();
|
|
}
|
|
|
|
Fid*
|
|
Exmkfid(Export *fs, int fid)
|
|
{
|
|
ulong h;
|
|
Fid *f, *nf;
|
|
|
|
nf = mallocz(sizeof(Fid));
|
|
if(nf == nil)
|
|
return nil;
|
|
lock(&fs->fidlock);
|
|
h = fid % Nfidhash;
|
|
for(f = fs->fid[h]; f != nil; f = f->next){
|
|
if(f->fid == fid){
|
|
unlock(&fs->fidlock);
|
|
free(nf);
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
nf->next = fs->fid[h];
|
|
if(nf->next != nil)
|
|
nf->next->last = &nf->next;
|
|
nf->last = &fs->fid[h];
|
|
fs->fid[h] = nf;
|
|
|
|
nf->fid = fid;
|
|
nf->ref = 1;
|
|
nf->attached = 1;
|
|
nf->offset = 0;
|
|
nf->chan = nil;
|
|
unlock(&fs->fidlock);
|
|
return nf;
|
|
}
|
|
|
|
Fid*
|
|
Exgetfid(Export *fs, int fid)
|
|
{
|
|
Fid *f;
|
|
ulong h;
|
|
|
|
lock(&fs->fidlock);
|
|
h = fid % Nfidhash;
|
|
for(f = fs->fid[h]; f; f = f->next) {
|
|
if(f->fid == fid){
|
|
if(f->attached == 0)
|
|
break;
|
|
f->ref++;
|
|
unlock(&fs->fidlock);
|
|
return f;
|
|
}
|
|
}
|
|
unlock(&fs->fidlock);
|
|
return nil;
|
|
}
|
|
|
|
void
|
|
Exputfid(Export *fs, Fid *f)
|
|
{
|
|
lock(&fs->fidlock);
|
|
f->ref--;
|
|
if(f->ref == 0 && f->attached == 0){
|
|
if(f->chan != nil)
|
|
cclose(f->chan);
|
|
f->chan = nil;
|
|
*f->last = f->next;
|
|
if(f->next != nil)
|
|
f->next->last = f->last;
|
|
unlock(&fs->fidlock);
|
|
free(f);
|
|
return;
|
|
}
|
|
unlock(&fs->fidlock);
|
|
}
|
|
|
|
char*
|
|
Exsession(Export *e, Fcall *rpc)
|
|
{
|
|
memset(rpc->authid, 0, sizeof(rpc->authid));
|
|
memset(rpc->authdom, 0, sizeof(rpc->authdom));
|
|
memset(rpc->chal, 0, sizeof(rpc->chal));
|
|
return nil;
|
|
}
|
|
|
|
char*
|
|
Exauth(Export *e, Fcall *f)
|
|
{
|
|
return "authentication not required";
|
|
}
|
|
|
|
char*
|
|
Exattach(Export *fs, Fcall *rpc)
|
|
{
|
|
Fid *f;
|
|
|
|
f = Exmkfid(fs, rpc->fid);
|
|
if(f == nil)
|
|
return Einuse;
|
|
if(waserror()){
|
|
f->attached = 0;
|
|
Exputfid(fs, f);
|
|
return up->errstr;
|
|
}
|
|
f->chan = clone(fs->root, nil);
|
|
poperror();
|
|
rpc->qid = f->chan->qid;
|
|
Exputfid(fs, f);
|
|
return nil;
|
|
}
|
|
|
|
char*
|
|
Exclone(Export *fs, Fcall *rpc)
|
|
{
|
|
Fid *f, *nf;
|
|
|
|
if(rpc->fid == rpc->newfid)
|
|
return Einuse;
|
|
f = Exgetfid(fs, rpc->fid);
|
|
if(f == nil)
|
|
return Enofid;
|
|
nf = Exmkfid(fs, rpc->newfid);
|
|
if(nf == nil){
|
|
Exputfid(fs, f);
|
|
return Einuse;
|
|
}
|
|
if(waserror()){
|
|
Exputfid(fs, f);
|
|
Exputfid(fs, nf);
|
|
return up->errstr;
|
|
}
|
|
nf->chan = clone(f->chan, nil);
|
|
poperror();
|
|
Exputfid(fs, f);
|
|
Exputfid(fs, nf);
|
|
return nil;
|
|
}
|
|
|
|
char*
|
|
Exclunk(Export *fs, Fcall *rpc)
|
|
{
|
|
Fid *f;
|
|
|
|
f = Exgetfid(fs, rpc->fid);
|
|
if(f != nil){
|
|
f->attached = 0;
|
|
Exputfid(fs, f);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
char*
|
|
Exwalk(Export *fs, Fcall *rpc)
|
|
{
|
|
Fid *f;
|
|
Chan *c;
|
|
|
|
f = Exgetfid(fs, rpc->fid);
|
|
if(f == nil)
|
|
return Enofid;
|
|
if(waserror()){
|
|
Exputfid(fs, f);
|
|
return up->errstr;
|
|
}
|
|
c = walk(f->chan, rpc->name, 1);
|
|
if(c == nil)
|
|
error(Enonexist);
|
|
poperror();
|
|
|
|
f->chan = c;
|
|
rpc->qid = c->qid;
|
|
Exputfid(fs, f);
|
|
return nil;
|
|
}
|
|
|
|
char*
|
|
Exopen(Export *fs, Fcall *rpc)
|
|
{
|
|
Fid *f;
|
|
Chan *c;
|
|
|
|
f = Exgetfid(fs, rpc->fid);
|
|
if(f == nil)
|
|
return Enofid;
|
|
if(waserror()){
|
|
Exputfid(fs, f);
|
|
return up->errstr;
|
|
}
|
|
c = f->chan;
|
|
c = (*devtab[c->type].open)(c, rpc->mode);
|
|
poperror();
|
|
|
|
f->chan = c;
|
|
f->offset = 0;
|
|
rpc->qid = f->chan->qid;
|
|
Exputfid(fs, f);
|
|
return nil;
|
|
}
|
|
|
|
char*
|
|
Excreate(Export *fs, Fcall *rpc)
|
|
{
|
|
Fid *f;
|
|
Chan *c;
|
|
|
|
f = Exgetfid(fs, rpc->fid);
|
|
if(f == nil)
|
|
return Enofid;
|
|
if(waserror()){
|
|
Exputfid(fs, f);
|
|
return up->errstr;
|
|
}
|
|
c = f->chan;
|
|
if(c->mnt && !(c->flag&CCREATE))
|
|
c = createdir(c);
|
|
(*devtab[c->type].create)(c, rpc->name, rpc->mode, rpc->perm);
|
|
poperror();
|
|
|
|
f->chan = c;
|
|
rpc->qid = f->chan->qid;
|
|
Exputfid(fs, f);
|
|
return nil;
|
|
}
|
|
|
|
char*
|
|
Exread(Export *fs, Fcall *rpc)
|
|
{
|
|
Fid *f;
|
|
Chan *c;
|
|
long off;
|
|
int dir, n, seek;
|
|
|
|
f = Exgetfid(fs, rpc->fid);
|
|
if(f == nil)
|
|
return Enofid;
|
|
|
|
c = f->chan;
|
|
dir = c->qid.path & CHDIR;
|
|
if(dir){
|
|
rpc->count -= rpc->count%DIRLEN;
|
|
if(rpc->offset%DIRLEN || rpc->count==0){
|
|
Exputfid(fs, f);
|
|
return Ereaddir;
|
|
}
|
|
if(f->offset > rpc->offset){
|
|
Exputfid(fs, f);
|
|
return Eseekdir;
|
|
}
|
|
}
|
|
|
|
if(waserror()) {
|
|
Exputfid(fs, f);
|
|
return up->errstr;
|
|
}
|
|
|
|
for(;;){
|
|
n = rpc->count;
|
|
seek = 0;
|
|
off = rpc->offset;
|
|
if(dir && f->offset != off){
|
|
off = f->offset;
|
|
n = rpc->offset - off;
|
|
if(n > MAXDIRREAD)
|
|
n = MAXDIRREAD;
|
|
seek = 1;
|
|
}
|
|
if(dir && c->mnt != nil)
|
|
n = unionread(c, rpc->data, n);
|
|
else{
|
|
c->offset = off;
|
|
n = (*devtab[c->type].read)(c, rpc->data, n, off);
|
|
}
|
|
if(n == 0 || !seek)
|
|
break;
|
|
f->offset = off + n;
|
|
c->offset += n;
|
|
}
|
|
rpc->count = n;
|
|
poperror();
|
|
Exputfid(fs, f);
|
|
return nil;
|
|
}
|
|
|
|
char*
|
|
Exwrite(Export *fs, Fcall *rpc)
|
|
{
|
|
Fid *f;
|
|
Chan *c;
|
|
|
|
f = Exgetfid(fs, rpc->fid);
|
|
if(f == nil)
|
|
return Enofid;
|
|
if(waserror()){
|
|
Exputfid(fs, f);
|
|
return up->errstr;
|
|
}
|
|
c = f->chan;
|
|
if(c->qid.path & CHDIR)
|
|
error(Eisdir);
|
|
rpc->count = (*devtab[c->type].write)(c, rpc->data, rpc->count, rpc->offset);
|
|
poperror();
|
|
Exputfid(fs, f);
|
|
return nil;
|
|
}
|
|
|
|
char*
|
|
Exstat(Export *fs, Fcall *rpc)
|
|
{
|
|
Fid *f;
|
|
Chan *c;
|
|
|
|
f = Exgetfid(fs, rpc->fid);
|
|
if(f == nil)
|
|
return Enofid;
|
|
if(waserror()){
|
|
Exputfid(fs, f);
|
|
return up->errstr;
|
|
}
|
|
c = f->chan;
|
|
(*devtab[c->type].stat)(c, rpc->stat);
|
|
poperror();
|
|
Exputfid(fs, f);
|
|
return nil;
|
|
}
|
|
|
|
char*
|
|
Exwstat(Export *fs, Fcall *rpc)
|
|
{
|
|
Fid *f;
|
|
Chan *c;
|
|
|
|
f = Exgetfid(fs, rpc->fid);
|
|
if(f == nil)
|
|
return Enofid;
|
|
if(waserror()){
|
|
Exputfid(fs, f);
|
|
return up->errstr;
|
|
}
|
|
c = f->chan;
|
|
(*devtab[c->type].wstat)(c, rpc->stat);
|
|
poperror();
|
|
Exputfid(fs, f);
|
|
return nil;
|
|
}
|
|
|
|
char*
|
|
Exremove(Export *fs, Fcall *rpc)
|
|
{
|
|
Fid *f;
|
|
Chan *c;
|
|
|
|
f = Exgetfid(fs, rpc->fid);
|
|
if(f == nil)
|
|
return Enofid;
|
|
if(waserror()){
|
|
Exputfid(fs, f);
|
|
return up->errstr;
|
|
}
|
|
c = f->chan;
|
|
(*devtab[c->type].remove)(c);
|
|
poperror();
|
|
|
|
/*
|
|
* chan is already clunked by remove.
|
|
* however, we need to recover the chan,
|
|
* and follow sysremove's lead in making to point to root.
|
|
*/
|
|
c->type = 0;
|
|
|
|
f->attached = 0;
|
|
Exputfid(fs, f);
|
|
return nil;
|
|
}
|