diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/ratfs/proto.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/ratfs/proto.c')
-rwxr-xr-x | sys/src/cmd/ratfs/proto.c | 512 |
1 files changed, 512 insertions, 0 deletions
diff --git a/sys/src/cmd/ratfs/proto.c b/sys/src/cmd/ratfs/proto.c new file mode 100755 index 000000000..7ed046fdd --- /dev/null +++ b/sys/src/cmd/ratfs/proto.c @@ -0,0 +1,512 @@ +#include "ratfs.h" + +/* + * 9P protocol interface + */ + +enum { + RELOAD = 0, /* commands written to ctl file */ + RDEBUG, + RNODEBUG, + RNONE, +}; + +static void rflush(Fcall*), rnop(Fcall*), + rauth(Fcall*), rattach(Fcall*), + rclone(Fcall*), rwalk(Fcall*), + rclwalk(Fcall*), ropen(Fcall*), + rcreate(Fcall*), rread(Fcall*), + rwrite(Fcall*), rclunk(Fcall*), + rremove(Fcall*), rstat(Fcall*), + rwstat(Fcall*), rversion(Fcall*); + +static Fid* newfid(int); +static void reply(Fcall*, char*); + +static void (*fcalls[])(Fcall*) = { + [Tversion] rversion, + [Tflush] rflush, + [Tauth] rauth, + [Tattach] rattach, + [Twalk] rwalk, + [Topen] ropen, + [Tcreate] rcreate, + [Tread] rread, + [Twrite] rwrite, + [Tclunk] rclunk, + [Tremove] rremove, + [Tstat] rstat, + [Twstat] rwstat, +}; + + +static Keyword cmds[] = { + "reload", RELOAD, + "debug", RDEBUG, + "nodebug", RNODEBUG, + 0, RNONE, +}; + +/* + * Main protocol loop + */ +void +io(void) +{ + Fcall rhdr; + int n; + + for(;;){ + n = read9pmsg(srvfd, rbuf, sizeof rbuf-1); + if(n <= 0) + fatal("mount read"); + if(convM2S(rbuf, n, &rhdr) == 0){ + if(debugfd >= 0) + fprint(2, "%s: malformed message\n", argv0); + continue; + } + + if(debugfd >= 0) + fprint(debugfd, "<-%F\n", &rhdr);/**/ + + if(!fcalls[rhdr.type]) + reply(&rhdr, "bad fcall type"); + else + (*fcalls[rhdr.type])(&rhdr); + } +} + +/* + * write a protocol reply to the client + */ +static void +reply(Fcall *r, char *error) +{ + int n; + + if(error == nil) + r->type++; + else { + r->type = Rerror; + r->ename = error; + } + if(debugfd >= 0) + fprint(debugfd, "->%F\n", r);/**/ + n = convS2M(r, rbuf, sizeof rbuf); + if(n == 0) + sysfatal("convS2M: %r"); + if(write(srvfd, rbuf, n) < 0) + sysfatal("reply: %r"); +} + + +/* + * lookup a fid. if not found, create a new one. + */ + +static Fid* +newfid(int fid) +{ + Fid *f, *ff; + + static Fid *fids; + + ff = 0; + for(f = fids; f; f = f->next){ + if(f->fid == fid){ + if(!f->busy) + f->node = 0; + return f; + } else if(!ff && !f->busy) + ff = f; + } + if(ff == 0){ + ff = mallocz(sizeof(*f), 1); + ff->next = fids; + fids = ff; + } + ff->node = 0; + ff->fid = fid; + return ff; +} + +static void +rversion(Fcall *f) +{ + f->version = "9P2000"; + if(f->msize > MAXRPC) + f->msize = MAXRPC; + reply(f, 0); +} + +static void +rauth(Fcall *f) +{ + reply(f, "ratfs: authentication not required"); +} + +static void +rflush(Fcall *f) +{ + reply(f, 0); +} + +static void +rattach(Fcall *f) +{ + Fid *fidp; + Dir *d; + + if((d=dirstat(conffile)) != nil && d->mtime > lastconftime) + getconf(); + free(d); + if((d=dirstat(ctlfile)) != nil && d->mtime > lastctltime) + reload(); + free(d); + cleantrusted(); + + fidp = newfid(f->fid); + fidp->busy = 1; + fidp->node = root; + fidp->name = root->d.name; + fidp->uid = atom(f->uname); + f->qid = root->d.qid; + reply(f,0); +} + +static void +rclone(Fcall *f) +{ + Fid *fidp, *nf; + + fidp = newfid(f->fid); + if(fidp->node && fidp->node->d.type == Dummynode){ + reply(f, "can't clone an address"); + return; + } + nf = newfid(f->newfid); + nf->busy = 1; + nf->node = fidp->node; + nf->uid = fidp->uid; + nf->name = fidp->name; + if(debugfd >= 0) + printfid(nf); + reply(f,0); +} + +static void +rwalk(Fcall *f) +{ + int i, j; + Fcall r; + Fid *fidp, *nf; + char *err; + + fidp = newfid(f->fid); + if(fidp->node && fidp->node->d.type == Dummynode){ + reply(f, "can't walk an address node"); + return; + } + if(f->fid == f->newfid) + nf = fidp; + else{ + nf = newfid(f->newfid); + nf->busy = 1; + nf->node = fidp->node; + nf->uid = fidp->uid; + nf->name = fidp->name; + if(debugfd >= 0) + printfid(nf); + } + + err = nil; + for(i=0; i<f->nwname; i++){ + err = walk(f->wname[i], nf); + if(err) + break; + r.wqid[i] = nf->node->d.qid; + } + + + if(i < f->nwname && f->fid != f->newfid){ + nf->busy = 0; + nf->node = 0; + nf->name = 0; + nf->uid = 0; + } + if(i > 0 && i < f->nwname && f->fid == f->newfid){ + /* + * try to put things back; + * we never get this sort of call from the kernel + */ + for(j=0; j<i; j++) + walk("..", nf); + } + memmove(f->wqid, r.wqid, sizeof f->wqid); + f->nwqid = i; + if(err && i==0) + reply(f, err); + else + reply(f, 0); +} + +/* + * We don't have to do full permission checking because most files + * have restricted semantics: + * The ctl file is only writable + * All others, including directories, are only readable + */ +static void +ropen(Fcall *f) +{ + Fid *fidp; + int mode; + + fidp = newfid(f->fid); + + if(debugfd >= 0) + printfid(fidp); + + mode = f->mode&(OREAD|OWRITE|ORDWR); + if(fidp->node->d.type == Ctlfile) { + if(mode != OWRITE) { + reply(f, "permission denied"); + return; + } + } else + if (mode != OREAD) { + reply(f, "permission denied or operation not supported"); + return; + } + + f->qid = fidp->node->d.qid; + fidp->open = 1; + reply(f, 0); +} + +static int +permitted(Fid *fp, Node *np, int mask) +{ + int mode; + + mode = np->d.mode; + return (fp->uid==np->d.uid && (mode&(mask<<6))) + || (fp->uid==np->d.gid && (mode&(mask<<3))) + || (mode&mask); +} + +/* + * creates are only allowed in the "trusted" subdirectory + * we also assume that the groupid == the uid + */ +static void +rcreate(Fcall *f) +{ + Fid *fidp; + Node *np; + + fidp = newfid(f->fid); + np = fidp->node; + if((np->d.mode&DMDIR) == 0){ + reply(f, "not a directory"); + return; + } + + if(!permitted(fidp, np, AWRITE)) { + reply(f, "permission denied"); + return; + } + + /* Ignore the supplied mode and force it to be non-writable */ + + np = newnode(np, f->name, Trustedtemp, 0444, trustedqid++); + if(trustedqid >= Qaddrfile) /* wrap QIDs */ + trustedqid = Qtrustedfile; + cidrparse(&np->ip, f->name); + f->qid = np->d.qid; + np->d.uid = fidp->uid; + np->d.gid = np->d.uid; + np->d.muid = np->d.muid; + fidp->node = np; + fidp->open = 1; + reply(f, 0); + return; +} + +/* + * only directories can be read. everthing else returns EOF. + */ +static void +rread(Fcall *f) +{ + long cnt; + Fid *fidp; + + cnt = f->count; + f->count = 0; + fidp = newfid(f->fid); + f->data = (char*)rbuf+IOHDRSZ; + if(fidp->open == 0) { + reply(f, "file not open"); + return; + } + if ((fidp->node->d.mode&DMDIR) == 0){ + reply(f, 0); /*EOF*/ + return; + } + if(cnt > MAXRPC) + cnt = MAXRPC; + + if(f->offset == 0) + fidp->dirindex = 0; + + switch(fidp->node->d.type) { + case Directory: + case Addrdir: + case Trusted: + f->count = dread(fidp, cnt); + break; + case IPaddr: + case Acctaddr: + f->count = hread(fidp, cnt); + break; + default: + reply(f, "can't read this type of file"); + return; + } + reply(f, 0); +} + + +/* + * only the 'ctl' file in the top level directory is writable + */ + +static void +rwrite(Fcall *f) +{ + Fid *fidp; + int n; + char *err, *argv[10]; + + fidp = newfid(f->fid); + if(fidp->node->d.mode & DMDIR){ + reply(f, "directories are not writable"); + return; + } + if(fidp->open == 0) { + reply(f, "file not open"); + return; + } + + if (!permitted(fidp, fidp->node, AWRITE)) { + reply(f, "permission denied"); + return; + } + + f->data[f->count] = 0; /* the extra byte in rbuf leaves room */ + n = tokenize(f->data, argv, 10); + err = 0; + switch(findkey(argv[0], cmds)){ + case RELOAD: + getconf(); + reload(); + break; + case RDEBUG: + if(n > 1){ + debugfd = create(argv[1], OWRITE, 0666); + if(debugfd < 0) + err = "create failed"; + } else + debugfd = 2; + break; + case RNODEBUG: + if(debugfd >= 0) + close(debugfd); + debugfd = -1; + break; + default: + err = "unknown command"; + break; + } + reply(f, err); +} + +static void +rclunk(Fcall *f) +{ + Fid *fidp; + + fidp = newfid(f->fid); + fidp->open = 0; + fidp->busy = 0; + fidp->node = 0; + fidp->name = 0; + fidp->uid = 0; + reply(f, 0); +} + +/* + * no files or directories are removable; this becomes clunk; + */ +static void +rremove(Fcall *f) +{ + Fid *fidp; + Node *dir, *np; + + fidp = newfid(f->fid); + + /* + * only trusted temporary files can be removed + * and only by their owner. + */ + if(fidp->node->d.type != Trustedtemp){ + reply(f, "can't be removed"); + return; + } + if(fidp->uid != fidp->node->d.uid){ + reply(f, "permission denied"); + return; + } + dir = fidp->node->parent; + for(np = dir->children; np; np = np->sibs) + if(np->sibs == fidp->node) + break; + if(np) + np->sibs = fidp->node->sibs; + else + dir->children = fidp->node->sibs; + dir->count--; + free(fidp->node); + fidp->node = 0; + fidp->open = 0; + fidp->busy = 0; + fidp->name = 0; + fidp->uid = 0; + reply(f, 0); +} + +static void +rstat(Fcall *f) +{ + Fid *fidp; + + fidp = newfid(f->fid); + if (fidp->node->d.type == Dummynode) + dummy.d.name = fidp->name; + f->stat = (uchar*)rbuf+4+1+2+2; /* knows about stat(5) */ + f->nstat = convD2M(&fidp->node->d, f->stat, MAXRPC); + if(f->nstat <= BIT16SZ) + reply(f, "ratfs: convD2M"); + else + reply(f, 0); + return; +} + +static void +rwstat(Fcall *f) +{ + reply(f, "wstat not implemented"); +} + |