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