📄 fsys.c
字号:
#include <u.h>#include <libc.h>#include <bio.h>#include <regexp.h>#include <thread.h>#include <auth.h>#include <fcall.h>#include <plumb.h>#include "plumber.h"enum{ Stack = 8*1024};typedef struct Dirtab Dirtab;typedef struct Fid Fid;typedef struct Holdq Holdq;typedef struct Readreq Readreq;typedef struct Sendreq Sendreq;struct Dirtab{ char *name; uchar type; uint qid; uint perm; int nopen; /* #fids open on this port */ Fid *fopen; Holdq *holdq; Readreq *readq; Sendreq *sendq;};struct Fid{ int fid; int busy; int open; int mode; Qid qid; Dirtab *dir; long offset; /* zeroed at beginning of each message, read or write */ char *writebuf; /* partial message written so far; offset tells how much */ Fid *next; Fid *nextopen;};struct Readreq{ Fid *fid; Fcall *fcall; uchar *buf; Readreq *next;};struct Sendreq{ int nfid; /* number of fids that should receive this message */ int nleft; /* number left that haven't received it */ Fid **fid; /* fid[nfid] */ Plumbmsg *msg; char *pack; /* plumbpack()ed message */ int npack; /* length of pack */ Sendreq *next;};struct Holdq{ Plumbmsg *msg; Holdq *next;};struct /* needed because incref() doesn't return value */{ Lock; int ref;} rulesref;enum{ DEBUG = 0, NDIR = 50, Nhash = 16, Qdir = 0, Qrules = 1, Qsend = 2, Qport = 3, NQID = Qport};static Dirtab dir[NDIR] ={ { ".", QTDIR, Qdir, 0500|DMDIR }, { "rules", QTFILE, Qrules, 0600 }, { "send", QTFILE, Qsend, 0200 },};static int ndir = NQID;static int srvfd;static int srvclosefd; /* rock for end of pipe to close */static int clockfd;static int clock;static Fid *fids[Nhash];static QLock readlock;static QLock queue;static char srvfile[128];static int messagesize = 8192+IOHDRSZ; /* good start */static void fsysproc(void*);static void fsysrespond(Fcall*, uchar*, char*);static Fid* newfid(int);static Fcall* fsysflush(Fcall*, uchar*, Fid*);static Fcall* fsysversion(Fcall*, uchar*, Fid*);static Fcall* fsysauth(Fcall*, uchar*, Fid*);static Fcall* fsysattach(Fcall*, uchar*, Fid*);static Fcall* fsyswalk(Fcall*, uchar*, Fid*);static Fcall* fsysopen(Fcall*, uchar*, Fid*);static Fcall* fsyscreate(Fcall*, uchar*, Fid*);static Fcall* fsysread(Fcall*, uchar*, Fid*);static Fcall* fsyswrite(Fcall*, uchar*, Fid*);static Fcall* fsysclunk(Fcall*, uchar*, Fid*);static Fcall* fsysremove(Fcall*, uchar*, Fid*);static Fcall* fsysstat(Fcall*, uchar*, Fid*);static Fcall* fsyswstat(Fcall*, uchar*, Fid*);Fcall* (*fcall[Tmax])(Fcall*, uchar*, Fid*) ={ [Tflush] = fsysflush, [Tversion] = fsysversion, [Tauth] = fsysauth, [Tattach] = fsysattach, [Twalk] = fsyswalk, [Topen] = fsysopen, [Tcreate] = fsyscreate, [Tread] = fsysread, [Twrite] = fsyswrite, [Tclunk] = fsysclunk, [Tremove]= fsysremove, [Tstat] = fsysstat, [Twstat] = fsyswstat,};char Ebadfcall[] = "bad fcall type";char Eperm[] = "permission denied";char Enomem[] = "malloc failed for buffer";char Enotdir[] = "not a directory";char Enoexist[] = "plumb file does not exist";char Eisdir[] = "file is a directory";char Ebadmsg[] = "bad plumb message format";char Enosuchport[] ="no such plumb port";char Enoport[] = "couldn't find destination for message";char Einuse[] = "file already open";/* * Add new port. A no-op if port already exists or is the null string */voidaddport(char *port){ int i; if(port == nil) return; for(i=NQID; i<ndir; i++) if(strcmp(port, dir[i].name) == 0) return; if(i == NDIR){ fprint(2, "plumb: too many ports; max %d\n", NDIR); return; } ndir++; dir[i].name = estrdup(port); dir[i].qid = i; dir[i].perm = 0400; nports++; ports = erealloc(ports, nports*sizeof(char*)); ports[nports-1] = dir[i].name;}static ulonggetclock(void){ char buf[32]; seek(clockfd, 0, 0); read(clockfd, buf, sizeof buf); return atoi(buf);}voidstartfsys(void){ int p[2], fd; fmtinstall('F', fcallfmt); clockfd = open("/dev/time", OREAD|OCEXEC); clock = getclock(); if(pipe(p) < 0) error("can't create pipe: %r"); /* 0 will be server end, 1 will be client end */ srvfd = p[0]; srvclosefd = p[1]; sprint(srvfile, "/srv/plumb.%s.%d", user, getpid()); if(putenv("plumbsrv", srvfile) < 0) error("can't write $plumbsrv: %r"); fd = create(srvfile, OWRITE|OCEXEC|ORCLOSE, 0600); if(fd < 0) error("can't create /srv file: %r"); if(fprint(fd, "%d", p[1]) <= 0) error("can't write /srv/file: %r"); /* leave fd open; ORCLOSE will take care of it */ procrfork(fsysproc, nil, Stack, RFFDG); close(p[0]); if(mount(p[1], -1, "/mnt/plumb", MREPL, "") < 0) error("can't mount /mnt/plumb: %r"); close(p[1]);}static voidfsysproc(void*){ int n; Fcall *t; Fid *f; uchar *buf; close(srvclosefd); srvclosefd = -1; t = nil; for(;;){ buf = malloc(messagesize); /* avoid memset of emalloc */ if(buf == nil) error("malloc failed: %r"); qlock(&readlock); n = read9pmsg(srvfd, buf, messagesize); if(n <= 0){ if(n < 0) error("i/o error on server channel"); threadexitsall("unmounted"); } if(readlock.head == nil) /* no other processes waiting to read; start one */ proccreate(fsysproc, nil, Stack); qunlock(&readlock); if(t == nil) t = emalloc(sizeof(Fcall)); if(convM2S(buf, n, t) != n) error("convert error in convM2S"); if(DEBUG) fprint(2, "<= %F\n", t); if(fcall[t->type] == nil) fsysrespond(t, buf, Ebadfcall); else{ if(t->type==Tversion || t->type==Tauth) f = nil; else f = newfid(t->fid); t = (*fcall[t->type])(t, buf, f); } }}static voidfsysrespond(Fcall *t, uchar *buf, char *err){ int n; if(err){ t->type = Rerror; t->ename = err; }else t->type++; if(buf == nil) buf = emalloc(messagesize); n = convS2M(t, buf, messagesize); if(n < 0) error("convert error in convS2M"); if(write(srvfd, buf, n) != n) error("write error in respond"); if(DEBUG) fprint(2, "=> %F\n", t); free(buf);}staticFid*newfid(int fid){ Fid *f, *ff, **fh; qlock(&queue); ff = nil; fh = &fids[fid&(Nhash-1)]; for(f=*fh; f; f=f->next) if(f->fid == fid) goto Return; else if(ff==nil && !f->busy) ff = f; if(ff){ ff->fid = fid; f = ff; goto Return; } f = emalloc(sizeof *f); f->fid = fid; f->next = *fh; *fh = f; Return: qunlock(&queue); return f;}static uintdostat(Dirtab *dir, uchar *buf, uint nbuf, uint clock){ Dir d; d.qid.type = dir->type; d.qid.path = dir->qid; d.qid.vers = 0; d.mode = dir->perm; d.length = 0; /* would be nice to do better */ d.name = dir->name; d.uid = user; d.gid = user; d.muid = user; d.atime = clock; d.mtime = clock; return convD2M(&d, buf, nbuf);}static voidqueuesend(Dirtab *d, Plumbmsg *m){ Sendreq *s, *t; Fid *f; int i; s = emalloc(sizeof(Sendreq)); s->nfid = d->nopen; s->nleft = s->nfid; s->fid = emalloc(s->nfid*sizeof(Fid*)); i = 0; /* build array of fids open on this channel */ for(f=d->fopen; f!=nil; f=f->nextopen) s->fid[i++] = f; s->msg = m; s->next = nil; /* link to end of queue; drainqueue() searches in sender order so this implements a FIFO */ for(t=d->sendq; t!=nil; t=t->next) if(t->next == nil) break; if(t == nil) d->sendq = s; else t->next = s;}static voidqueueread(Dirtab *d, Fcall *t, uchar *buf, Fid *f){ Readreq *r; r = emalloc(sizeof(Readreq)); r->fcall = t; r->buf = buf; r->fid = f; r->next = d->readq; d->readq = r;}static voiddrainqueue(Dirtab *d){ Readreq *r, *nextr, *prevr; Sendreq *s, *nexts, *prevs; int i, n; prevs = nil; for(s=d->sendq; s!=nil; s=nexts){ nexts = s->next; for(i=0; i<s->nfid; i++){ prevr = nil; for(r=d->readq; r!=nil; r=nextr){ nextr = r->next; if(r->fid == s->fid[i]){ /* pack the message if necessary */ if(s->pack == nil) s->pack = plumbpack(s->msg, &s->npack); /* exchange the stuff... */ r->fcall->data = s->pack+r->fid->offset; n = s->npack - r->fid->offset; if(n > messagesize-IOHDRSZ) n = messagesize-IOHDRSZ; if(n > r->fcall->count) n = r->fcall->count; r->fcall->count = n; fsysrespond(r->fcall, r->buf, nil); r->fid->offset += n; if(r->fid->offset >= s->npack){ /* message transferred; delete this fid from send queue */ r->fid->offset = 0; s->fid[i] = nil; s->nleft--; } /* delete read request from queue */ if(prevr) prevr->next = r->next; else d->readq = r->next; free(r->fcall); free(r); break; }else prevr = r; } } /* if no fids left, delete this send from queue */ if(s->nleft == 0){ free(s->fid); plumbfree(s->msg); free(s->pack); if(prevs) prevs->next = s->next; else d->sendq = s->next; free(s); }else prevs = s; }}/* can't flush a send because they are always answered synchronously */static voidflushqueue(Dirtab *d, int oldtag){ Readreq *r, *prevr; prevr = nil; for(r=d->readq; r!=nil; r=r->next){ if(oldtag == r->fcall->tag){ /* delete read request from queue */ if(prevr) prevr->next = r->next; else d->readq = r->next; free(r->fcall); free(r->buf); free(r); return; } prevr = r; }}/* remove messages awaiting delivery to now-closing fid */static voidremovesenders(Dirtab *d, Fid *fid){ Sendreq *s, *nexts, *prevs; int i; prevs = nil; for(s=d->sendq; s!=nil; s=nexts){ nexts = s->next; for(i=0; i<s->nfid; i++) if(fid == s->fid[i]){ /* delete this fid from send queue */ s->fid[i] = nil; s->nleft--; break; } /* if no fids left, delete this send from queue */ if(s->nleft == 0){ free(s->fid); plumbfree(s->msg); free(s->pack); if(prevs) prevs->next = s->next; else d->sendq = s->next; free(s); }else prevs = s;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -