398477c9434069a588e434a2ccda2a0ff2875192
[akaros.git] / kern / drivers / dev / pipe.c
1 /* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
2  * Portions Copyright © 1997-1999 Vita Nuova Limited
3  * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
4  *                                (www.vitanuova.com)
5  * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
6  *
7  * Modified for the Akaros operating system:
8  * Copyright (c) 2013-2014 The Regents of the University of California
9  * Copyright (c) 2013-2015 Google Inc.
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a copy
12  * of this software and associated documentation files (the "Software"), to deal
13  * in the Software without restriction, including without limitation the rights
14  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15  * copies of the Software, and to permit persons to whom the Software is
16  * furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE. */
28
29 #include <vfs.h>
30 #include <kfs.h>
31 #include <slab.h>
32 #include <kmalloc.h>
33 #include <kref.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <assert.h>
37 #include <error.h>
38 #include <cpio.h>
39 #include <pmap.h>
40 #include <smp.h>
41 #include <ip.h>
42
43 struct dev pipedevtab;
44
45 static char *devname(void)
46 {
47         return pipedevtab.name;
48 }
49
50 typedef struct Pipe Pipe;
51 struct Pipe {
52         qlock_t qlock;
53         Pipe *next;
54         struct kref ref;
55         uint32_t path;
56         struct queue *q[2];
57         int qref[2];
58         struct dirtab *pipedir;
59         char *user;
60 };
61
62 static struct {
63         spinlock_t lock;
64         uint32_t path;
65         int pipeqsize;
66 } pipealloc;
67
68 enum {
69         Qdir,
70         Qdata0,
71         Qdata1,
72 };
73
74 static
75 struct dirtab pipedir[] = {
76         {".", {Qdir, 0, QTDIR}, 0, DMDIR | 0500},
77         {"data", {Qdata0}, 0, 0660},
78         {"data1", {Qdata1}, 0, 0660},
79 };
80
81 static void freepipe(Pipe * p)
82 {
83         if (p != NULL) {
84                 kfree(p->user);
85                 kfree(p->q[0]);
86                 kfree(p->q[1]);
87                 kfree(p->pipedir);
88                 kfree(p);
89         }
90 }
91
92 static void pipe_release(struct kref *kref)
93 {
94         Pipe *pipe = container_of(kref, Pipe, ref);
95         freepipe(pipe);
96 }
97
98 static void pipeinit(void)
99 {
100         pipealloc.pipeqsize = 32 * 1024;
101 }
102
103 /*
104  *  create a pipe, no streams are created until an open
105  */
106 static struct chan *pipeattach(char *spec)
107 {
108         ERRSTACK(2);
109         Pipe *p;
110         struct chan *c;
111
112         c = devattach(devname(), spec);
113         p = kzmalloc(sizeof(Pipe), 0);
114         if (p == 0)
115                 error(ENOMEM, NULL);
116         if (waserror()) {
117                 freepipe(p);
118                 nexterror();
119         }
120         p->pipedir = kzmalloc(sizeof(pipedir), 0);
121         if (p->pipedir == 0)
122                 error(ENOMEM, NULL);
123         memmove(p->pipedir, pipedir, sizeof(pipedir));
124         kstrdup(&p->user, current->user);
125         kref_init(&p->ref, pipe_release, 1);
126         qlock_init(&p->qlock);
127
128         p->q[0] = qopen(pipealloc.pipeqsize, Qcoalesce, 0, 0);
129         if (p->q[0] == 0)
130                 error(ENOMEM, NULL);
131         p->q[1] = qopen(pipealloc.pipeqsize, Qcoalesce, 0, 0);
132         if (p->q[1] == 0)
133                 error(ENOMEM, NULL);
134         poperror();
135
136         spin_lock(&(&pipealloc)->lock);
137         p->path = ++pipealloc.path;
138         spin_unlock(&(&pipealloc)->lock);
139
140         c->qid.path = NETQID(2 * p->path, Qdir);
141         c->qid.vers = 0;
142         c->qid.type = QTDIR;
143         c->aux = p;
144         c->dev = 0;
145         return c;
146 }
147
148 static int
149 pipegen(struct chan *c, char *unused,
150                 struct dirtab *tab, int ntab, int i, struct dir *dp)
151 {
152         int id, len;
153         struct qid qid;
154         Pipe *p;
155
156         if (i == DEVDOTDOT) {
157                 devdir(c, c->qid, devname(), 0, eve, 0555, dp);
158                 return 1;
159         }
160         i++;    /* skip . */
161         if (tab == 0 || i >= ntab)
162                 return -1;
163         tab += i;
164         p = c->aux;
165         switch (NETTYPE(tab->qid.path)) {
166                 case Qdata0:
167                         len = qlen(p->q[0]);
168                         break;
169                 case Qdata1:
170                         len = qlen(p->q[1]);
171                         break;
172                 default:
173                         len = tab->length;
174                         break;
175         }
176         id = NETID(c->qid.path);
177         qid.path = NETQID(id, tab->qid.path);
178         qid.vers = 0;
179         qid.type = QTFILE;
180         devdir(c, qid, tab->name, len, eve, tab->perm, dp);
181         return 1;
182 }
183
184 static struct walkqid *pipewalk(struct chan *c, struct chan *nc, char **name,
185                                                                 int nname)
186 {
187         struct walkqid *wq;
188         Pipe *p;
189
190         p = c->aux;
191         wq = devwalk(c, nc, name, nname, p->pipedir, ARRAY_SIZE(pipedir), pipegen);
192         if (wq != NULL && wq->clone != NULL && wq->clone != c) {
193                 qlock(&p->qlock);
194                 kref_get(&p->ref, 1);
195                 if (c->flag & COPEN) {
196                         switch (NETTYPE(c->qid.path)) {
197                                 case Qdata0:
198                                         p->qref[0]++;
199                                         break;
200                                 case Qdata1:
201                                         p->qref[1]++;
202                                         break;
203                         }
204                 }
205                 qunlock(&p->qlock);
206         }
207         return wq;
208 }
209
210 static int pipestat(struct chan *c, uint8_t * db, int n)
211 {
212         Pipe *p;
213         struct dir dir;
214         struct dirtab *tab;
215
216         p = c->aux;
217         tab = p->pipedir;
218
219         switch (NETTYPE(c->qid.path)) {
220                 case Qdir:
221                         devdir(c, c->qid, ".", 0, eve, DMDIR | 0555, &dir);
222                         break;
223                 case Qdata0:
224                         devdir(c, c->qid, tab[1].name, qlen(p->q[0]), eve, tab[1].perm,
225                                    &dir);
226                         break;
227                 case Qdata1:
228                         devdir(c, c->qid, tab[2].name, qlen(p->q[1]), eve, tab[2].perm,
229                                    &dir);
230                         break;
231                 default:
232                         panic("pipestat");
233         }
234         n = convD2M(&dir, db, n);
235         if (n < BIT16SZ)
236                 error(ENODATA, NULL);
237         return n;
238 }
239
240 /*
241  *  if the stream doesn't exist, create it
242  */
243 static struct chan *pipeopen(struct chan *c, int omode)
244 {
245         ERRSTACK(2);
246         Pipe *p;
247
248         if (c->qid.type & QTDIR) {
249                 if (omode & O_WRITE)
250                         error(EINVAL, "Can only open directories O_READ, mode is %o oct",
251                                   omode);
252                 c->mode = openmode(omode);
253                 c->flag |= COPEN;
254                 c->offset = 0;
255                 return c;
256         }
257
258         openmode(omode);        /* check it */
259
260         p = c->aux;
261         qlock(&p->qlock);
262         if (waserror()) {
263                 qunlock(&p->qlock);
264                 nexterror();
265         }
266         switch (NETTYPE(c->qid.path)) {
267                 case Qdata0:
268                         devpermcheck(p->user, p->pipedir[1].perm, omode);
269                         p->qref[0]++;
270                         break;
271                 case Qdata1:
272                         devpermcheck(p->user, p->pipedir[2].perm, omode);
273                         p->qref[1]++;
274                         break;
275         }
276         poperror();
277         qunlock(&p->qlock);
278
279         c->mode = openmode(omode);
280         c->flag |= COPEN;
281         c->offset = 0;
282         c->iounit = qiomaxatomic;
283         return c;
284 }
285
286 static void pipeclose(struct chan *c)
287 {
288         Pipe *p;
289
290         p = c->aux;
291         qlock(&p->qlock);
292
293         if (c->flag & COPEN) {
294                 /*
295                  *  closing either side hangs up the stream
296                  */
297                 switch (NETTYPE(c->qid.path)) {
298                         case Qdata0:
299                                 p->qref[0]--;
300                                 if (p->qref[0] == 0) {
301                                         qhangup(p->q[1], 0);
302                                         qclose(p->q[0]);
303                                 }
304                                 break;
305                         case Qdata1:
306                                 p->qref[1]--;
307                                 if (p->qref[1] == 0) {
308                                         qhangup(p->q[0], 0);
309                                         qclose(p->q[1]);
310                                 }
311                                 break;
312                 }
313         }
314
315         /*
316          *  if both sides are closed, they are reusable
317          */
318         if (p->qref[0] == 0 && p->qref[1] == 0) {
319                 qreopen(p->q[0]);
320                 qreopen(p->q[1]);
321         }
322
323         qunlock(&p->qlock);
324         /*
325          *  free the structure on last close
326          */
327         kref_put(&p->ref);
328 }
329
330 static long piperead(struct chan *c, void *va, long n, int64_t ignored)
331 {
332         Pipe *p;
333
334         p = c->aux;
335
336         switch (NETTYPE(c->qid.path)) {
337                 case Qdir:
338                         return devdirread(c, va, n, p->pipedir, ARRAY_SIZE(pipedir),
339                                                           pipegen);
340                 case Qdata0:
341                         return qread(p->q[0], va, n);
342                 case Qdata1:
343                         return qread(p->q[1], va, n);
344                 default:
345                         panic("piperead");
346         }
347         return -1;      /* not reached */
348 }
349
350 static struct block *pipebread(struct chan *c, long n, uint32_t offset)
351 {
352         Pipe *p;
353
354         p = c->aux;
355
356         switch (NETTYPE(c->qid.path)) {
357                 case Qdata0:
358                         return qbread(p->q[0], n);
359                 case Qdata1:
360                         return qbread(p->q[1], n);
361         }
362
363         return devbread(c, n, offset);
364 }
365
366 /*
367  *  A write to a closed pipe causes an EPIPE error to be thrown.
368  */
369 static long pipewrite(struct chan *c, void *va, long n, int64_t ignored)
370 {
371         ERRSTACK(2);
372         Pipe *p;
373         //Prog *r;
374
375         if (waserror()) {
376                 /* avoid exceptions when pipe is a mounted queue */
377                 if ((c->flag & CMSG) == 0) {
378 /*
379                         r = up->iprog;
380                         if(r != NULL && r->kill == NULL)
381                                 r->kill = "write on closed pipe";
382 */
383                 }
384                 set_errno(EPIPE);
385                 nexterror();
386         }
387
388         p = c->aux;
389
390         switch (NETTYPE(c->qid.path)) {
391                 case Qdata0:
392                         n = qwrite(p->q[1], va, n);
393                         break;
394
395                 case Qdata1:
396                         n = qwrite(p->q[0], va, n);
397                         break;
398
399                 default:
400                         panic("pipewrite");
401         }
402
403         poperror();
404         return n;
405 }
406
407 static long pipebwrite(struct chan *c, struct block *bp, uint32_t junk)
408 {
409         ERRSTACK(2);
410         long n;
411         Pipe *p;
412         //Prog *r;
413
414         if (waserror()) {
415                 /* avoid exceptions when pipe is a mounted queue */
416 /*
417                 if((c->flag & CMSG) == 0) {
418                         r = up->iprog;
419                         if(r != NULL && r->kill == NULL)
420                                 r->kill = "write on closed pipe";
421                 }
422 */
423                 set_errno(EPIPE);
424                 nexterror();
425         }
426
427         p = c->aux;
428         switch (NETTYPE(c->qid.path)) {
429                 case Qdata0:
430                         n = qbwrite(p->q[1], bp);
431                         break;
432
433                 case Qdata1:
434                         n = qbwrite(p->q[0], bp);
435                         break;
436
437                 default:
438                         n = 0;
439                         panic("pipebwrite");
440         }
441
442         poperror();
443         return n;
444 }
445
446 static int pipewstat(struct chan *c, uint8_t *dp, int n)
447 {
448         ERRSTACK(2);
449         struct dir *d;
450         Pipe *p;
451         int d1;
452
453         if (c->qid.type & QTDIR)
454                 error(EPERM, NULL);
455         p = c->aux;
456         if (strcmp(current->user, p->user) != 0)
457                 error(EPERM, NULL);
458         d = kzmalloc(sizeof(*d) + n, 0);
459         if (waserror()) {
460                 kfree(d);
461                 nexterror();
462         }
463         n = convM2D(dp, n, d, (char *)&d[1]);
464         if (n == 0)
465                 error(ENODATA, NULL);
466         d1 = NETTYPE(c->qid.path) == Qdata1;
467         if (!emptystr(d->name)) {
468                 validwstatname(d->name);
469                 if (strlen(d->name) >= KNAMELEN)
470                         error(ENAMETOOLONG, NULL);
471                 if (strncmp(p->pipedir[1 + !d1].name, d->name, KNAMELEN) == 0)
472                         error(EEXIST, NULL);
473                 strncpy(p->pipedir[1 + d1].name, d->name, KNAMELEN);
474         }
475         if (d->mode != ~0UL)
476                 p->pipedir[d1 + 1].perm = d->mode & 0777;
477         poperror();
478         kfree(d);
479         return n;
480 }
481
482 struct dev pipedevtab __devtab = {
483         .name = "pipe",
484
485         .reset = devreset,
486         .init = pipeinit,
487         .shutdown = devshutdown,
488         .attach = pipeattach,
489         .walk = pipewalk,
490         .stat = pipestat,
491         .open = pipeopen,
492         .create = devcreate,
493         .close = pipeclose,
494         .read = piperead,
495         .bread = pipebread,
496         .write = pipewrite,
497         .bwrite = pipebwrite,
498         .remove = devremove,
499         .wstat = pipewstat,
500         .power = devpower,
501         .chaninfo = devchaninfo,
502 };