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