da2dba8c95fed187d37dcf619f2ab994f76dab31
[akaros.git] / kern / drivers / dev / pipe.c
1 // INFERNO
2 #include <vfs.h>
3 #include <kfs.h>
4 #include <slab.h>
5 #include <kmalloc.h>
6 #include <kref.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <assert.h>
10 #include <error.h>
11 #include <cpio.h>
12 #include <pmap.h>
13 #include <smp.h>
14 #include <ip.h>
15
16 typedef struct Pipe Pipe;
17 struct Pipe {
18         qlock_t qlock;
19         Pipe *next;
20         struct kref ref;
21         uint32_t path;
22         struct queue *q[2];
23         int qref[2];
24         struct dirtab *pipedir;
25         char *user;
26 };
27
28 static struct {
29         spinlock_t lock;
30         uint32_t path;
31         int pipeqsize;
32 } pipealloc;
33
34 enum {
35         Qdir,
36         Qdata0,
37         Qdata1,
38 };
39
40 static
41 struct dirtab pipedir[] = {
42         {".", {Qdir, 0, QTDIR}, 0, DMDIR | 0500},
43         {"data", {Qdata0}, 0, 0660},
44         {"data1", {Qdata1}, 0, 0660},
45 };
46
47 static void freepipe(Pipe * p)
48 {
49         if (p != NULL) {
50                 kfree(p->user);
51                 kfree(p->q[0]);
52                 kfree(p->q[1]);
53                 kfree(p->pipedir);
54                 kfree(p);
55         }
56 }
57
58 static void pipe_release(struct kref *kref)
59 {
60         Pipe *pipe = container_of(kref, Pipe, ref);
61         qunlock(&pipe->qlock);
62         freepipe(pipe);
63 }
64
65 static void pipeinit(void)
66 {
67         pipealloc.pipeqsize = 32 * 1024;
68 }
69
70 /*
71  *  create a pipe, no streams are created until an open
72  */
73 static struct chan *pipeattach(char *spec)
74 {
75         ERRSTACK(2);
76         Pipe *p;
77         struct chan *c;
78
79         c = devattach('|', spec);
80         p = kzmalloc(sizeof(Pipe), 0);
81         if (p == 0)
82                 error(Enomem);
83         if (waserror()) {
84                 freepipe(p);
85                 nexterror();
86         }
87         p->pipedir = kzmalloc(sizeof(pipedir), 0);
88         if (p->pipedir == 0)
89                 error(Enomem);
90         memmove(p->pipedir, pipedir, sizeof(pipedir));
91         kstrdup(&p->user, current->user);
92         kref_init(&p->ref, pipe_release, 1);
93         qlock_init(&p->qlock);
94
95         p->q[0] = qopen(pipealloc.pipeqsize, Qcoalesce, 0, 0);
96         if (p->q[0] == 0)
97                 error(Enomem);
98         p->q[1] = qopen(pipealloc.pipeqsize, Qcoalesce, 0, 0);
99         if (p->q[1] == 0)
100                 error(Enomem);
101         poperror();
102
103         spin_lock(&(&pipealloc)->lock);
104         p->path = ++pipealloc.path;
105         spin_unlock(&(&pipealloc)->lock);
106
107         c->qid.path = NETQID(2 * p->path, Qdir);
108         c->qid.vers = 0;
109         c->qid.type = QTDIR;
110         c->aux = p;
111         c->dev = 0;
112         return c;
113 }
114
115 static int
116 pipegen(struct chan *c, char *unused,
117                 struct dirtab *tab, int ntab, int i, struct dir *dp)
118 {
119         int id, len;
120         struct qid qid;
121         Pipe *p;
122
123         if (i == DEVDOTDOT) {
124                 devdir(c, c->qid, "#|", 0, eve, 0555, dp);
125                 return 1;
126         }
127         i++;    /* skip . */
128         if (tab == 0 || i >= ntab)
129                 return -1;
130         tab += i;
131         p = c->aux;
132         switch (NETTYPE(tab->qid.path)) {
133                 case Qdata0:
134                         len = qlen(p->q[0]);
135                         break;
136                 case Qdata1:
137                         len = qlen(p->q[1]);
138                         break;
139                 default:
140                         len = tab->length;
141                         break;
142         }
143         id = NETID(c->qid.path);
144         qid.path = NETQID(id, tab->qid.path);
145         qid.vers = 0;
146         qid.type = QTFILE;
147         devdir(c, qid, tab->name, len, eve, tab->perm, dp);
148         return 1;
149 }
150
151 static struct walkqid *pipewalk(struct chan *c, struct chan *nc, char **name,
152                                                                 int nname)
153 {
154         struct walkqid *wq;
155         Pipe *p;
156
157         p = c->aux;
158         wq = devwalk(c, nc, name, nname, p->pipedir, ARRAY_SIZE(pipedir), pipegen);
159         if (wq != NULL && wq->clone != NULL && wq->clone != c) {
160                 qlock(&p->qlock);
161                 kref_get(&p->ref, 1);
162                 if (c->flag & COPEN) {
163                         switch (NETTYPE(c->qid.path)) {
164                                 case Qdata0:
165                                         p->qref[0]++;
166                                         break;
167                                 case Qdata1:
168                                         p->qref[1]++;
169                                         break;
170                         }
171                 }
172                 qunlock(&p->qlock);
173         }
174         return wq;
175 }
176
177 static int pipestat(struct chan *c, uint8_t * db, int n)
178 {
179         Pipe *p;
180         struct dir dir;
181         struct dirtab *tab;
182
183         p = c->aux;
184         tab = p->pipedir;
185
186         switch (NETTYPE(c->qid.path)) {
187                 case Qdir:
188                         devdir(c, c->qid, ".", 0, eve, DMDIR | 0555, &dir);
189                         break;
190                 case Qdata0:
191                         devdir(c, c->qid, tab[1].name, qlen(p->q[0]), eve, tab[1].perm,
192                                    &dir);
193                         break;
194                 case Qdata1:
195                         devdir(c, c->qid, tab[2].name, qlen(p->q[1]), eve, tab[2].perm,
196                                    &dir);
197                         break;
198                 default:
199                         panic("pipestat");
200         }
201         n = convD2M(&dir, db, n);
202         if (n < BIT16SZ)
203                 error(Eshortstat);
204         return n;
205 }
206
207 /*
208  *  if the stream doesn't exist, create it
209  */
210 static struct chan *pipeopen(struct chan *c, int omode)
211 {
212         ERRSTACK(2);
213         Pipe *p;
214
215         if (c->qid.type & QTDIR) {
216                 if (!IS_RDONLY(omode))
217                         error("Can only open directories OREAD, mode is %o oct", omode);
218                 c->mode = openmode(omode);
219                 c->flag |= COPEN;
220                 c->offset = 0;
221                 return c;
222         }
223
224         openmode(omode);        /* check it */
225
226         p = c->aux;
227         qlock(&p->qlock);
228         if (waserror()) {
229                 qunlock(&p->qlock);
230                 nexterror();
231         }
232         switch (NETTYPE(c->qid.path)) {
233                 case Qdata0:
234                         devpermcheck(p->user, p->pipedir[1].perm, omode);
235                         p->qref[0]++;
236                         break;
237                 case Qdata1:
238                         devpermcheck(p->user, p->pipedir[2].perm, omode);
239                         p->qref[1]++;
240                         break;
241         }
242         poperror();
243         qunlock(&p->qlock);
244
245         c->mode = openmode(omode);
246         c->flag |= COPEN;
247         c->offset = 0;
248         c->iounit = qiomaxatomic;
249         return c;
250 }
251
252 static void pipeclose(struct chan *c)
253 {
254         Pipe *p;
255
256         p = c->aux;
257         qlock(&p->qlock);
258
259         if (c->flag & COPEN) {
260                 /*
261                  *  closing either side hangs up the stream
262                  */
263                 switch (NETTYPE(c->qid.path)) {
264                         case Qdata0:
265                                 p->qref[0]--;
266                                 if (p->qref[0] == 0) {
267                                         qhangup(p->q[1], 0);
268                                         qclose(p->q[0]);
269                                 }
270                                 break;
271                         case Qdata1:
272                                 p->qref[1]--;
273                                 if (p->qref[1] == 0) {
274                                         qhangup(p->q[0], 0);
275                                         qclose(p->q[1]);
276                                 }
277                                 break;
278                 }
279         }
280
281         /*
282          *  if both sides are closed, they are reusable
283          */
284         if (p->qref[0] == 0 && p->qref[1] == 0) {
285                 qreopen(p->q[0]);
286                 qreopen(p->q[1]);
287         }
288
289         /*
290          *  free the structure on last close
291          */
292         kref_put(&p->ref);
293         qunlock(&p->qlock);
294 }
295
296 static long piperead(struct chan *c, void *va, long n, int64_t ignored)
297 {
298         Pipe *p;
299
300         p = c->aux;
301
302         switch (NETTYPE(c->qid.path)) {
303                 case Qdir:
304                         return devdirread(c, va, n, p->pipedir, ARRAY_SIZE(pipedir),
305                                                           pipegen);
306                 case Qdata0:
307                         return qread(p->q[0], va, n);
308                 case Qdata1:
309                         return qread(p->q[1], va, n);
310                 default:
311                         panic("piperead");
312         }
313         return -1;      /* not reached */
314 }
315
316 static struct block *pipebread(struct chan *c, long n, uint32_t offset)
317 {
318         Pipe *p;
319
320         p = c->aux;
321
322         switch (NETTYPE(c->qid.path)) {
323                 case Qdata0:
324                         return qbread(p->q[0], n);
325                 case Qdata1:
326                         return qbread(p->q[1], n);
327         }
328
329         return devbread(c, n, offset);
330 }
331
332 /*
333  *  a write to a closed pipe causes an exception to be sent to
334  *  the prog.
335  */
336 static long pipewrite(struct chan *c, void *va, long n, int64_t ignored)
337 {
338         ERRSTACK(2);
339         Pipe *p;
340         //Prog *r;
341
342         if (waserror()) {
343                 /* avoid exceptions when pipe is a mounted queue */
344                 if ((c->flag & CMSG) == 0) {
345 /*
346                         r = up->iprog;
347                         if(r != NULL && r->kill == NULL)
348                                 r->kill = "write on closed pipe";
349 */
350                 }
351                 nexterror();
352         }
353
354         p = c->aux;
355
356         switch (NETTYPE(c->qid.path)) {
357                 case Qdata0:
358                         n = qwrite(p->q[1], va, n);
359                         break;
360
361                 case Qdata1:
362                         n = qwrite(p->q[0], va, n);
363                         break;
364
365                 default:
366                         panic("pipewrite");
367         }
368
369         poperror();
370         return n;
371 }
372
373 static long pipebwrite(struct chan *c, struct block *bp, uint32_t junk)
374 {
375         ERRSTACK(2);
376         long n;
377         Pipe *p;
378         //Prog *r;
379
380         if (waserror()) {
381                 /* avoid exceptions when pipe is a mounted queue */
382 /*
383                 if((c->flag & CMSG) == 0) {
384                         r = up->iprog;
385                         if(r != NULL && r->kill == NULL)
386                                 r->kill = "write on closed pipe";
387                 }
388 */
389                 nexterror();
390         }
391
392         p = c->aux;
393         switch (NETTYPE(c->qid.path)) {
394                 case Qdata0:
395                         n = qbwrite(p->q[1], bp);
396                         break;
397
398                 case Qdata1:
399                         n = qbwrite(p->q[0], bp);
400                         break;
401
402                 default:
403                         n = 0;
404                         panic("pipebwrite");
405         }
406
407         poperror();
408         return n;
409 }
410
411 static int pipewstat(struct chan *c, uint8_t * dp, int n)
412 {
413         ERRSTACK(2);
414         struct dir *d;
415         Pipe *p;
416         int d1;
417
418         if (c->qid.type & QTDIR)
419                 error(Eperm);
420         p = c->aux;
421         if (strcmp(current->user, p->user) != 0)
422                 error(Eperm);
423         d = kzmalloc(sizeof(*d) + n, 0);
424         if (waserror()) {
425                 kfree(d);
426                 nexterror();
427         }
428         n = convM2D(dp, n, d, (char *)&d[1]);
429         if (n == 0)
430                 error(Eshortstat);
431         d1 = NETTYPE(c->qid.path) == Qdata1;
432         if (!emptystr(d->name)) {
433                 validwstatname(d->name);
434                 if (strlen(d->name) >= KNAMELEN)
435                         error(Efilename);
436                 if (strcmp(p->pipedir[1 + !d1].name, d->name) == 0)
437                         error(Eexist);
438                 strncpy(p->pipedir[1 + d1].name, d->name,
439                                 MIN(KNAMELEN, sizeof(p->pipedir[1 + d1].name, d->name)));
440         }
441         if (d->mode != ~0UL)
442                 p->pipedir[d1 + 1].perm = d->mode & 0777;
443         poperror();
444         kfree(d);
445         return n;
446 }
447
448 struct dev pipedevtab __devtab = {
449         '|',
450         "pipe",
451
452         devreset,
453         pipeinit,
454         devshutdown,
455         pipeattach,
456         pipewalk,
457         pipestat,
458         pipeopen,
459         devcreate,
460         pipeclose,
461         piperead,
462         pipebread,
463         pipewrite,
464         pipebwrite,
465         devremove,
466         pipewstat,
467         devpower,
468         devchaninfo,
469 };