| // INFERNO |
| #include <vfs.h> |
| #include <kfs.h> |
| #include <slab.h> |
| #include <kmalloc.h> |
| #include <kref.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <assert.h> |
| #include <error.h> |
| #include <cpio.h> |
| #include <pmap.h> |
| #include <smp.h> |
| #include <ip.h> |
| |
| typedef struct Pipe Pipe; |
| struct Pipe { |
| qlock_t qlock; |
| Pipe *next; |
| struct kref ref; |
| uint32_t path; |
| struct queue *q[2]; |
| int qref[2]; |
| struct dirtab *pipedir; |
| char *user; |
| }; |
| |
| static struct { |
| spinlock_t lock; |
| uint32_t path; |
| int pipeqsize; |
| } pipealloc; |
| |
| enum { |
| Qdir, |
| Qdata0, |
| Qdata1, |
| }; |
| |
| static |
| struct dirtab pipedir[] = { |
| {".", {Qdir, 0, QTDIR}, 0, DMDIR | 0500}, |
| {"data", {Qdata0}, 0, 0660}, |
| {"data1", {Qdata1}, 0, 0660}, |
| }; |
| |
| static void freepipe(Pipe * p) |
| { |
| if (p != NULL) { |
| kfree(p->user); |
| kfree(p->q[0]); |
| kfree(p->q[1]); |
| kfree(p->pipedir); |
| kfree(p); |
| } |
| } |
| |
| static void pipe_release(struct kref *kref) |
| { |
| Pipe *pipe = container_of(kref, Pipe, ref); |
| qunlock(&pipe->qlock); |
| freepipe(pipe); |
| } |
| |
| static void pipeinit(void) |
| { |
| pipealloc.pipeqsize = 32 * 1024; |
| } |
| |
| /* |
| * create a pipe, no streams are created until an open |
| */ |
| static struct chan *pipeattach(char *spec) |
| { |
| ERRSTACK(2); |
| Pipe *p; |
| struct chan *c; |
| |
| c = devattach('|', spec); |
| p = kzmalloc(sizeof(Pipe), 0); |
| if (p == 0) |
| error(Enomem); |
| if (waserror()) { |
| freepipe(p); |
| nexterror(); |
| } |
| p->pipedir = kzmalloc(sizeof(pipedir), 0); |
| if (p->pipedir == 0) |
| error(Enomem); |
| memmove(p->pipedir, pipedir, sizeof(pipedir)); |
| kstrdup(&p->user, current->user); |
| kref_init(&p->ref, pipe_release, 1); |
| qlock_init(&p->qlock); |
| |
| p->q[0] = qopen(pipealloc.pipeqsize, Qcoalesce, 0, 0); |
| if (p->q[0] == 0) |
| error(Enomem); |
| p->q[1] = qopen(pipealloc.pipeqsize, Qcoalesce, 0, 0); |
| if (p->q[1] == 0) |
| error(Enomem); |
| poperror(); |
| |
| spin_lock(&(&pipealloc)->lock); |
| p->path = ++pipealloc.path; |
| spin_unlock(&(&pipealloc)->lock); |
| |
| c->qid.path = NETQID(2 * p->path, Qdir); |
| c->qid.vers = 0; |
| c->qid.type = QTDIR; |
| c->aux = p; |
| c->dev = 0; |
| return c; |
| } |
| |
| static int |
| pipegen(struct chan *c, char *unused, |
| struct dirtab *tab, int ntab, int i, struct dir *dp) |
| { |
| int id, len; |
| struct qid qid; |
| Pipe *p; |
| |
| if (i == DEVDOTDOT) { |
| devdir(c, c->qid, "#|", 0, eve, 0555, dp); |
| return 1; |
| } |
| i++; /* skip . */ |
| if (tab == 0 || i >= ntab) |
| return -1; |
| tab += i; |
| p = c->aux; |
| switch (NETTYPE(tab->qid.path)) { |
| case Qdata0: |
| len = qlen(p->q[0]); |
| break; |
| case Qdata1: |
| len = qlen(p->q[1]); |
| break; |
| default: |
| len = tab->length; |
| break; |
| } |
| id = NETID(c->qid.path); |
| qid.path = NETQID(id, tab->qid.path); |
| qid.vers = 0; |
| qid.type = QTFILE; |
| devdir(c, qid, tab->name, len, eve, tab->perm, dp); |
| return 1; |
| } |
| |
| static struct walkqid *pipewalk(struct chan *c, struct chan *nc, char **name, |
| int nname) |
| { |
| struct walkqid *wq; |
| Pipe *p; |
| |
| p = c->aux; |
| wq = devwalk(c, nc, name, nname, p->pipedir, ARRAY_SIZE(pipedir), pipegen); |
| if (wq != NULL && wq->clone != NULL && wq->clone != c) { |
| qlock(&p->qlock); |
| kref_get(&p->ref, 1); |
| if (c->flag & COPEN) { |
| switch (NETTYPE(c->qid.path)) { |
| case Qdata0: |
| p->qref[0]++; |
| break; |
| case Qdata1: |
| p->qref[1]++; |
| break; |
| } |
| } |
| qunlock(&p->qlock); |
| } |
| return wq; |
| } |
| |
| static int pipestat(struct chan *c, uint8_t * db, int n) |
| { |
| Pipe *p; |
| struct dir dir; |
| struct dirtab *tab; |
| |
| p = c->aux; |
| tab = p->pipedir; |
| |
| switch (NETTYPE(c->qid.path)) { |
| case Qdir: |
| devdir(c, c->qid, ".", 0, eve, DMDIR | 0555, &dir); |
| break; |
| case Qdata0: |
| devdir(c, c->qid, tab[1].name, qlen(p->q[0]), eve, tab[1].perm, |
| &dir); |
| break; |
| case Qdata1: |
| devdir(c, c->qid, tab[2].name, qlen(p->q[1]), eve, tab[2].perm, |
| &dir); |
| break; |
| default: |
| panic("pipestat"); |
| } |
| n = convD2M(&dir, db, n); |
| if (n < BIT16SZ) |
| error(Eshortstat); |
| return n; |
| } |
| |
| /* |
| * if the stream doesn't exist, create it |
| */ |
| static struct chan *pipeopen(struct chan *c, int omode) |
| { |
| ERRSTACK(2); |
| Pipe *p; |
| |
| if (c->qid.type & QTDIR) { |
| if (!IS_RDONLY(omode)) |
| error("Can only open directories OREAD, mode is %o oct", omode); |
| c->mode = openmode(omode); |
| c->flag |= COPEN; |
| c->offset = 0; |
| return c; |
| } |
| |
| openmode(omode); /* check it */ |
| |
| p = c->aux; |
| qlock(&p->qlock); |
| if (waserror()) { |
| qunlock(&p->qlock); |
| nexterror(); |
| } |
| switch (NETTYPE(c->qid.path)) { |
| case Qdata0: |
| devpermcheck(p->user, p->pipedir[1].perm, omode); |
| p->qref[0]++; |
| break; |
| case Qdata1: |
| devpermcheck(p->user, p->pipedir[2].perm, omode); |
| p->qref[1]++; |
| break; |
| } |
| poperror(); |
| qunlock(&p->qlock); |
| |
| c->mode = openmode(omode); |
| c->flag |= COPEN; |
| c->offset = 0; |
| c->iounit = qiomaxatomic; |
| return c; |
| } |
| |
| static void pipeclose(struct chan *c) |
| { |
| Pipe *p; |
| |
| p = c->aux; |
| qlock(&p->qlock); |
| |
| if (c->flag & COPEN) { |
| /* |
| * closing either side hangs up the stream |
| */ |
| switch (NETTYPE(c->qid.path)) { |
| case Qdata0: |
| p->qref[0]--; |
| if (p->qref[0] == 0) { |
| qhangup(p->q[1], 0); |
| qclose(p->q[0]); |
| } |
| break; |
| case Qdata1: |
| p->qref[1]--; |
| if (p->qref[1] == 0) { |
| qhangup(p->q[0], 0); |
| qclose(p->q[1]); |
| } |
| break; |
| } |
| } |
| |
| /* |
| * if both sides are closed, they are reusable |
| */ |
| if (p->qref[0] == 0 && p->qref[1] == 0) { |
| qreopen(p->q[0]); |
| qreopen(p->q[1]); |
| } |
| |
| /* |
| * free the structure on last close |
| */ |
| kref_put(&p->ref); |
| qunlock(&p->qlock); |
| } |
| |
| static long piperead(struct chan *c, void *va, long n, int64_t ignored) |
| { |
| Pipe *p; |
| |
| p = c->aux; |
| |
| switch (NETTYPE(c->qid.path)) { |
| case Qdir: |
| return devdirread(c, va, n, p->pipedir, ARRAY_SIZE(pipedir), |
| pipegen); |
| case Qdata0: |
| return qread(p->q[0], va, n); |
| case Qdata1: |
| return qread(p->q[1], va, n); |
| default: |
| panic("piperead"); |
| } |
| return -1; /* not reached */ |
| } |
| |
| static struct block *pipebread(struct chan *c, long n, uint32_t offset) |
| { |
| Pipe *p; |
| |
| p = c->aux; |
| |
| switch (NETTYPE(c->qid.path)) { |
| case Qdata0: |
| return qbread(p->q[0], n); |
| case Qdata1: |
| return qbread(p->q[1], n); |
| } |
| |
| return devbread(c, n, offset); |
| } |
| |
| /* |
| * a write to a closed pipe causes an exception to be sent to |
| * the prog. |
| */ |
| static long pipewrite(struct chan *c, void *va, long n, int64_t ignored) |
| { |
| ERRSTACK(2); |
| Pipe *p; |
| //Prog *r; |
| |
| if (waserror()) { |
| /* avoid exceptions when pipe is a mounted queue */ |
| if ((c->flag & CMSG) == 0) { |
| /* |
| r = up->iprog; |
| if(r != NULL && r->kill == NULL) |
| r->kill = "write on closed pipe"; |
| */ |
| } |
| nexterror(); |
| } |
| |
| p = c->aux; |
| |
| switch (NETTYPE(c->qid.path)) { |
| case Qdata0: |
| n = qwrite(p->q[1], va, n); |
| break; |
| |
| case Qdata1: |
| n = qwrite(p->q[0], va, n); |
| break; |
| |
| default: |
| panic("pipewrite"); |
| } |
| |
| poperror(); |
| return n; |
| } |
| |
| static long pipebwrite(struct chan *c, struct block *bp, uint32_t junk) |
| { |
| ERRSTACK(2); |
| long n; |
| Pipe *p; |
| //Prog *r; |
| |
| if (waserror()) { |
| /* avoid exceptions when pipe is a mounted queue */ |
| /* |
| if((c->flag & CMSG) == 0) { |
| r = up->iprog; |
| if(r != NULL && r->kill == NULL) |
| r->kill = "write on closed pipe"; |
| } |
| */ |
| nexterror(); |
| } |
| |
| p = c->aux; |
| switch (NETTYPE(c->qid.path)) { |
| case Qdata0: |
| n = qbwrite(p->q[1], bp); |
| break; |
| |
| case Qdata1: |
| n = qbwrite(p->q[0], bp); |
| break; |
| |
| default: |
| n = 0; |
| panic("pipebwrite"); |
| } |
| |
| poperror(); |
| return n; |
| } |
| |
| static int pipewstat(struct chan *c, uint8_t * dp, int n) |
| { |
| ERRSTACK(2); |
| struct dir *d; |
| Pipe *p; |
| int d1; |
| |
| if (c->qid.type & QTDIR) |
| error(Eperm); |
| p = c->aux; |
| if (strcmp(current->user, p->user) != 0) |
| error(Eperm); |
| d = kzmalloc(sizeof(*d) + n, 0); |
| if (waserror()) { |
| kfree(d); |
| nexterror(); |
| } |
| n = convM2D(dp, n, d, (char *)&d[1]); |
| if (n == 0) |
| error(Eshortstat); |
| d1 = NETTYPE(c->qid.path) == Qdata1; |
| if (!emptystr(d->name)) { |
| validwstatname(d->name); |
| if (strlen(d->name) >= KNAMELEN) |
| error(Efilename); |
| if (strcmp(p->pipedir[1 + !d1].name, d->name) == 0) |
| error(Eexist); |
| strncpy(p->pipedir[1 + d1].name, d->name, |
| MIN(KNAMELEN, sizeof(p->pipedir[1 + d1].name, d->name))); |
| } |
| if (d->mode != ~0UL) |
| p->pipedir[d1 + 1].perm = d->mode & 0777; |
| poperror(); |
| kfree(d); |
| return n; |
| } |
| |
| struct dev pipedevtab __devtab = { |
| '|', |
| "pipe", |
| |
| devreset, |
| pipeinit, |
| devshutdown, |
| pipeattach, |
| pipewalk, |
| pipestat, |
| pipeopen, |
| devcreate, |
| pipeclose, |
| piperead, |
| pipebread, |
| pipewrite, |
| pipebwrite, |
| devremove, |
| pipewstat, |
| devpower, |
| devchaninfo, |
| }; |