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