1 /*        $NetBSD: framebuf.c,v 1.37 2022/04/19 20:32:17 rillig Exp $ */
2 
3 /*
4  * Copyright (c) 2007  Antti Kantee.  All Rights Reserved.
5  *
6  * Development of this software was supported by the
7  * Finnish Cultural Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 /*
32  * The event portion of this code is a twisty maze of pointers,
33  * flags, yields and continues.  Sincere aplogies.
34  */
35 
36 #include <sys/cdefs.h>
37 #if !defined(lint)
38 __RCSID("$NetBSD: framebuf.c,v 1.37 2022/04/19 20:32:17 rillig Exp $");
39 #endif /* !lint */
40 
41 #include <sys/types.h>
42 #include <sys/queue.h>
43 
44 #include <assert.h>
45 #include <errno.h>
46 #include <poll.h>
47 #include <puffs.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51 
52 #include "puffs_priv.h"
53 
54 struct puffs_framebuf {
55           struct puffs_cc *pcc;         /* pcc to continue with */
56           /* OR */
57           puffs_framev_cb fcb;          /* non-blocking callback */
58           void *fcb_arg;                /* argument for previous */
59 
60           uint8_t *buf;                 /* buffer base */
61           size_t len;                   /* total length */
62 
63           size_t offset;                /* cursor, telloff() */
64           size_t maxoff;                /* maximum offset for data, tellsize() */
65 
66           volatile int rv;    /* errno value */
67 
68           int       istat;
69 
70           TAILQ_ENTRY(puffs_framebuf) pfb_entries;
71 };
72 #define ISTAT_NODESTROY       0x01      /* indestructible by framebuf_destroy() */
73 #define ISTAT_INTERNAL        0x02      /* never leaves library                           */
74 #define ISTAT_NOREPLY         0x04      /* nuke after sending                             */
75 #define ISTAT_DIRECT          0x08      /* receive directly, no moveinfo        */
76 
77 #define ISTAT_ONQUEUE         ISTAT_NODESTROY     /* alias */
78 
79 #define PUFBUF_INCRALLOC 4096
80 #define PUFBUF_REMAIN(p) (p->len - p->offset)
81 
82 /* for poll/kqueue */
83 struct puffs_fbevent {
84           struct puffs_cc     *pcc;
85           int what;
86           volatile int rv;
87 
88           LIST_ENTRY(puffs_fbevent) pfe_entries;
89 };
90 
91 static struct puffs_fctrl_io *
getfiobyfd(struct puffs_usermount * pu,int fd)92 getfiobyfd(struct puffs_usermount *pu, int fd)
93 {
94           struct puffs_fctrl_io *fio;
95 
96           LIST_FOREACH(fio, &pu->pu_ios, fio_entries)
97                     if (fio->io_fd == fd)
98                               return fio;
99           return NULL;
100 }
101 
102 struct puffs_framebuf *
puffs_framebuf_make(void)103 puffs_framebuf_make(void)
104 {
105           struct puffs_framebuf *pufbuf;
106 
107           pufbuf = malloc(sizeof(struct puffs_framebuf));
108           if (pufbuf == NULL)
109                     return NULL;
110           memset(pufbuf, 0, sizeof(struct puffs_framebuf));
111 
112           pufbuf->buf = malloc(PUFBUF_INCRALLOC);
113           if (pufbuf->buf == NULL) {
114                     free(pufbuf);
115                     return NULL;
116           }
117           pufbuf->len = PUFBUF_INCRALLOC;
118 
119           puffs_framebuf_recycle(pufbuf);
120           return pufbuf;
121 }
122 
123 void
puffs_framebuf_destroy(struct puffs_framebuf * pufbuf)124 puffs_framebuf_destroy(struct puffs_framebuf *pufbuf)
125 {
126 
127           assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
128 
129           free(pufbuf->buf);
130           free(pufbuf);
131 }
132 
133 void
puffs_framebuf_recycle(struct puffs_framebuf * pufbuf)134 puffs_framebuf_recycle(struct puffs_framebuf *pufbuf)
135 {
136 
137           assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
138 
139           pufbuf->offset = 0;
140           pufbuf->maxoff = 0;
141           pufbuf->istat = 0;
142 }
143 
144 static int
reservespace(struct puffs_framebuf * pufbuf,size_t off,size_t wantsize)145 reservespace(struct puffs_framebuf *pufbuf, size_t off, size_t wantsize)
146 {
147           size_t incr;
148           void *nd;
149 
150           if (off <= pufbuf->len && pufbuf->len - off >= wantsize)
151                     return 0;
152 
153           for (incr = PUFBUF_INCRALLOC;
154               pufbuf->len + incr < off + wantsize;
155               incr += PUFBUF_INCRALLOC)
156                     continue;
157 
158           nd = realloc(pufbuf->buf, pufbuf->len + incr);
159           if (nd == NULL)
160                     return -1;
161 
162           pufbuf->buf = nd;
163           pufbuf->len += incr;
164 
165           return 0;
166 }
167 
168 int
puffs_framebuf_dup(struct puffs_framebuf * pb,struct puffs_framebuf ** pbp)169 puffs_framebuf_dup(struct puffs_framebuf *pb, struct puffs_framebuf **pbp)
170 {
171           struct puffs_framebuf *newpb;
172 
173           newpb = puffs_framebuf_make();
174           if (newpb == NULL) {
175                     errno = ENOMEM;
176                     return -1;
177           }
178           memcpy(newpb, pb, sizeof(struct puffs_framebuf));
179 
180           newpb->buf = NULL;
181           newpb->len = 0;
182           if (reservespace(newpb, 0, pb->maxoff) == -1) {
183                     puffs_framebuf_destroy(newpb);
184                     return -1;
185           }
186 
187           memcpy(newpb->buf, pb->buf, pb->maxoff);
188           newpb->istat = 0;
189           *pbp = newpb;
190 
191           return 0;
192 }
193 
194 int
puffs_framebuf_reserve_space(struct puffs_framebuf * pufbuf,size_t wantsize)195 puffs_framebuf_reserve_space(struct puffs_framebuf *pufbuf, size_t wantsize)
196 {
197 
198           return reservespace(pufbuf, pufbuf->offset, wantsize);
199 }
200 
201 int
puffs_framebuf_putdata(struct puffs_framebuf * pufbuf,const void * data,size_t dlen)202 puffs_framebuf_putdata(struct puffs_framebuf *pufbuf,
203           const void *data, size_t dlen)
204 {
205 
206           if (PUFBUF_REMAIN(pufbuf) < dlen)
207                     if (puffs_framebuf_reserve_space(pufbuf, dlen) == -1)
208                               return -1;
209 
210           memcpy(pufbuf->buf + pufbuf->offset, data, dlen);
211           pufbuf->offset += dlen;
212 
213           if (pufbuf->offset > pufbuf->maxoff)
214                     pufbuf->maxoff = pufbuf->offset;
215 
216           return 0;
217 }
218 
219 int
puffs_framebuf_putdata_atoff(struct puffs_framebuf * pufbuf,size_t offset,const void * data,size_t dlen)220 puffs_framebuf_putdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
221           const void *data, size_t dlen)
222 {
223 
224           if (reservespace(pufbuf, offset, dlen) == -1)
225                     return -1;
226 
227           memcpy(pufbuf->buf + offset, data, dlen);
228 
229           if (offset + dlen > pufbuf->maxoff)
230                     pufbuf->maxoff = offset + dlen;
231 
232           return 0;
233 }
234 
235 int
puffs_framebuf_getdata(struct puffs_framebuf * pufbuf,void * data,size_t dlen)236 puffs_framebuf_getdata(struct puffs_framebuf *pufbuf, void *data, size_t dlen)
237 {
238 
239           if (pufbuf->maxoff < pufbuf->offset + dlen) {
240                     errno = ENOBUFS;
241                     return -1;
242           }
243 
244           memcpy(data, pufbuf->buf + pufbuf->offset, dlen);
245           pufbuf->offset += dlen;
246 
247           return 0;
248 }
249 
250 int
puffs_framebuf_getdata_atoff(struct puffs_framebuf * pufbuf,size_t offset,void * data,size_t dlen)251 puffs_framebuf_getdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
252           void *data, size_t dlen)
253 {
254 
255           if (pufbuf->maxoff < offset + dlen) {
256                     errno = ENOBUFS;
257                     return -1;
258           }
259 
260           memcpy(data, pufbuf->buf + offset, dlen);
261           return 0;
262 }
263 
264 size_t
puffs_framebuf_telloff(struct puffs_framebuf * pufbuf)265 puffs_framebuf_telloff(struct puffs_framebuf *pufbuf)
266 {
267 
268           return pufbuf->offset;
269 }
270 
271 size_t
puffs_framebuf_tellsize(struct puffs_framebuf * pufbuf)272 puffs_framebuf_tellsize(struct puffs_framebuf *pufbuf)
273 {
274 
275           return pufbuf->maxoff;
276 }
277 
278 size_t
puffs_framebuf_remaining(struct puffs_framebuf * pufbuf)279 puffs_framebuf_remaining(struct puffs_framebuf *pufbuf)
280 {
281 
282           return puffs_framebuf_tellsize(pufbuf) - puffs_framebuf_telloff(pufbuf);
283 }
284 
285 int
puffs_framebuf_seekset(struct puffs_framebuf * pufbuf,size_t newoff)286 puffs_framebuf_seekset(struct puffs_framebuf *pufbuf, size_t newoff)
287 {
288 
289           if (reservespace(pufbuf, newoff, 0) == -1)
290                     return -1;
291 
292           pufbuf->offset = newoff;
293           return 0;
294 }
295 
296 int
puffs_framebuf_getwindow(struct puffs_framebuf * pufbuf,size_t winoff,void ** data,size_t * dlen)297 puffs_framebuf_getwindow(struct puffs_framebuf *pufbuf, size_t winoff,
298           void **data, size_t *dlen)
299 {
300           size_t winlen;
301 
302 #ifdef WINTESTING
303           winlen = MIN(*dlen, 32);
304 #else
305           winlen = *dlen;
306 #endif
307 
308           if (reservespace(pufbuf, winoff, winlen) == -1)
309                     return -1;
310 
311           *data = pufbuf->buf + winoff;
312           if (pufbuf->maxoff < winoff + winlen)
313                     pufbuf->maxoff = winoff + winlen;
314 
315           return 0;
316 }
317 
318 void *
puffs__framebuf_getdataptr(struct puffs_framebuf * pufbuf)319 puffs__framebuf_getdataptr(struct puffs_framebuf *pufbuf)
320 {
321 
322           return pufbuf->buf;
323 }
324 
325 static void
errnotify(struct puffs_usermount * pu,struct puffs_framebuf * pufbuf,int error)326 errnotify(struct puffs_usermount *pu, struct puffs_framebuf *pufbuf, int error)
327 {
328 
329           pufbuf->rv = error;
330           if (pufbuf->pcc) {
331                     puffs__goto(pufbuf->pcc);
332           } else if (pufbuf->fcb) {
333                     pufbuf->istat &= ~ISTAT_NODESTROY;
334                     pufbuf->fcb(pu, pufbuf, pufbuf->fcb_arg, error);
335           } else {
336                     pufbuf->istat &= ~ISTAT_NODESTROY;
337                     puffs_framebuf_destroy(pufbuf);
338           }
339 }
340 
341 #define GETFIO(fd)                                                              \
342 do {                                                                                      \
343           fio = getfiobyfd(pu, fd);                                             \
344           if (fio == NULL) {                                                    \
345                     errno = EINVAL;                                                       \
346                     return -1;                                                            \
347           }                                                                               \
348           if (fio->stat & FIO_WRGONE) {                                         \
349                     errno = ESHUTDOWN;                                          \
350                     return -1;                                                            \
351           }                                                                               \
352 } while (0)
353 
354 int
puffs_framev_enqueue_cc(struct puffs_cc * pcc,int fd,struct puffs_framebuf * pufbuf,int flags)355 puffs_framev_enqueue_cc(struct puffs_cc *pcc, int fd,
356           struct puffs_framebuf *pufbuf, int flags)
357 {
358           struct puffs_usermount *pu = pcc->pcc_pu;
359           struct puffs_fctrl_io *fio;
360 
361           /*
362            * Technically we shouldn't allow this if RDGONE, but it's
363            * difficult to trap write close without allowing writes.
364            * And besides, there's probably a disconnect sequence in
365            * the protocol, so unexpectedly getting a closed fd is
366            * most likely an error condition.
367            */
368           GETFIO(fd);
369 
370           pufbuf->pcc = pcc;
371           pufbuf->fcb = NULL;
372           pufbuf->fcb_arg = NULL;
373 
374           pufbuf->offset = 0;
375           pufbuf->istat |= ISTAT_NODESTROY;
376 
377           if (flags & PUFFS_FBQUEUE_URGENT)
378                     TAILQ_INSERT_HEAD(&fio->snd_qing, pufbuf, pfb_entries);
379           else
380                     TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
381 
382           puffs_cc_yield(pcc);
383           if (pufbuf->rv) {
384                     pufbuf->istat &= ~ISTAT_NODESTROY;
385                     errno = pufbuf->rv;
386                     return -1;
387           }
388 
389           return 0;
390 }
391 
392 int
puffs_framev_enqueue_cb(struct puffs_usermount * pu,int fd,struct puffs_framebuf * pufbuf,puffs_framev_cb fcb,void * arg,int flags)393 puffs_framev_enqueue_cb(struct puffs_usermount *pu, int fd,
394           struct puffs_framebuf *pufbuf, puffs_framev_cb fcb, void *arg,
395           int flags)
396 {
397           struct puffs_fctrl_io *fio;
398 
399           /* see enqueue_cc */
400           GETFIO(fd);
401 
402           pufbuf->pcc = NULL;
403           pufbuf->fcb = fcb;
404           pufbuf->fcb_arg = arg;
405 
406           pufbuf->offset = 0;
407           pufbuf->istat |= ISTAT_NODESTROY;
408 
409           if (flags & PUFFS_FBQUEUE_URGENT)
410                     TAILQ_INSERT_HEAD(&fio->snd_qing, pufbuf, pfb_entries);
411           else
412                     TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
413 
414           return 0;
415 }
416 
417 int
puffs_framev_enqueue_justsend(struct puffs_usermount * pu,int fd,struct puffs_framebuf * pufbuf,int reply,int flags)418 puffs_framev_enqueue_justsend(struct puffs_usermount *pu, int fd,
419           struct puffs_framebuf *pufbuf, int reply, int flags)
420 {
421           struct puffs_fctrl_io *fio;
422 
423           assert((pufbuf->istat & ISTAT_INTERNAL) == 0);
424 
425           GETFIO(fd);
426 
427           pufbuf->pcc = NULL;
428           pufbuf->fcb = NULL;
429           pufbuf->fcb_arg = NULL;
430 
431           pufbuf->offset = 0;
432           pufbuf->istat |= ISTAT_NODESTROY;
433           if (!reply)
434                     pufbuf->istat |= ISTAT_NOREPLY;
435 
436           if (flags & PUFFS_FBQUEUE_URGENT)
437                     TAILQ_INSERT_HEAD(&fio->snd_qing, pufbuf, pfb_entries);
438           else
439                     TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
440 
441           return 0;
442 }
443 
444 /* ARGSUSED */
445 int
puffs_framev_enqueue_directreceive(struct puffs_cc * pcc,int fd,struct puffs_framebuf * pufbuf,int flags)446 puffs_framev_enqueue_directreceive(struct puffs_cc *pcc, int fd,
447           struct puffs_framebuf *pufbuf, int flags /* used in the future */)
448 {
449           struct puffs_usermount *pu = pcc->pcc_pu;
450           struct puffs_fctrl_io *fio;
451 
452           assert((pufbuf->istat & ISTAT_INTERNAL) == 0);
453 
454           fio = getfiobyfd(pu, fd);
455           if (fio == NULL) {
456                     errno = EINVAL;
457                     return -1;
458           }
459 
460           /* XXX: should have cur_in queue */
461           assert(fio->cur_in == NULL);
462           fio->cur_in = pufbuf;
463 
464           pufbuf->pcc = pcc;
465           pufbuf->fcb = NULL;
466           pufbuf->fcb_arg = NULL;
467 
468           pufbuf->offset = 0;
469           pufbuf->istat |= ISTAT_NODESTROY | ISTAT_DIRECT;
470 
471           puffs_cc_yield(pcc);
472           pufbuf->istat &= ~ISTAT_NODESTROY; /* XXX: not the right place */
473           if (pufbuf->rv) {
474                     errno = pufbuf->rv;
475                     return -1;
476           }
477 
478           return 0;
479 }
480 
481 int
puffs_framev_enqueue_directsend(struct puffs_cc * pcc,int fd,struct puffs_framebuf * pufbuf,int flags)482 puffs_framev_enqueue_directsend(struct puffs_cc *pcc, int fd,
483           struct puffs_framebuf *pufbuf, int flags)
484 {
485           struct puffs_usermount *pu = pcc->pcc_pu;
486           struct puffs_fctrl_io *fio;
487 
488           assert((pufbuf->istat & ISTAT_INTERNAL) == 0);
489 
490           if (flags & PUFFS_FBQUEUE_URGENT)
491                     abort(); /* EOPNOTSUPP for now */
492 
493           GETFIO(fd);
494 
495           pufbuf->pcc = pcc;
496           pufbuf->fcb = NULL;
497           pufbuf->fcb_arg = NULL;
498 
499           pufbuf->offset = 0;
500           pufbuf->istat |= ISTAT_NODESTROY | ISTAT_DIRECT;
501 
502           TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
503 
504           puffs_cc_yield(pcc);
505           if (pufbuf->rv) {
506                     pufbuf->istat &= ~ISTAT_NODESTROY;
507                     errno = pufbuf->rv;
508                     return -1;
509           }
510 
511           return 0;
512 }
513 
514 int
puffs_framev_framebuf_ccpromote(struct puffs_framebuf * pufbuf,struct puffs_cc * pcc)515 puffs_framev_framebuf_ccpromote(struct puffs_framebuf *pufbuf,
516           struct puffs_cc *pcc)
517 {
518 
519           if ((pufbuf->istat & ISTAT_ONQUEUE) == 0) {
520                     errno = EBUSY;
521                     return -1;
522           }
523 
524           pufbuf->pcc = pcc;
525           pufbuf->fcb = NULL;
526           pufbuf->fcb_arg = NULL;
527           pufbuf->istat &= ~ISTAT_NOREPLY;
528 
529           puffs_cc_yield(pcc);
530 
531           return 0;
532 }
533 
534 int
puffs_framev_enqueue_waitevent(struct puffs_cc * pcc,int fd,int * what)535 puffs_framev_enqueue_waitevent(struct puffs_cc *pcc, int fd, int *what)
536 {
537           struct puffs_usermount *pu = pcc->pcc_pu;
538           struct puffs_fctrl_io *fio;
539           struct puffs_fbevent feb;
540           struct kevent kev;
541           int rv, svwhat;
542 
543           svwhat = *what;
544 
545           if (*what == 0) {
546                     errno = EINVAL;
547                     return -1;
548           }
549 
550           fio = getfiobyfd(pu, fd);
551           if (fio == NULL) {
552                     errno = EINVAL;
553                     return -1;
554           }
555 
556           feb.pcc = pcc;
557           feb.what = *what & (PUFFS_FBIO_READ|PUFFS_FBIO_WRITE|PUFFS_FBIO_ERROR);
558 
559           if (*what & PUFFS_FBIO_READ)
560                     if ((fio->stat & FIO_ENABLE_R) == 0)
561                               EV_SET(&kev, fd, EVFILT_READ, EV_ENABLE, 0, 0, fio);
562 
563           if (kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL) == -1)
564                     return -1;
565 
566           if (*what & PUFFS_FBIO_READ)
567                     fio->rwait++;
568           if (*what & PUFFS_FBIO_WRITE)
569                     fio->wwait++;
570 
571           LIST_INSERT_HEAD(&fio->ev_qing, &feb, pfe_entries);
572           puffs_cc_yield(pcc);
573 
574           assert(svwhat == *what);
575 
576           if (*what & PUFFS_FBIO_READ) {
577                     fio->rwait--;
578                     if (fio->rwait == 0 && (fio->stat & FIO_ENABLE_R) == 0) {
579                               EV_SET(&kev, fd, EVFILT_READ, EV_DISABLE, 0, 0, fio);
580                               rv = kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
581 #if 0
582                               if (rv != 0)
583                                         /* XXXXX oh dear */;
584 #endif
585                     }
586           }
587           if (*what & PUFFS_FBIO_WRITE)
588                     fio->wwait--;
589 
590           if (feb.rv == 0) {
591                     *what = feb.what;
592                     rv = 0;
593           } else {
594                     *what = PUFFS_FBIO_ERROR;
595                     errno = feb.rv;
596                     rv = -1;
597           }
598 
599           return rv;
600 }
601 
602 void
puffs__framev_notify(struct puffs_fctrl_io * fio,int what)603 puffs__framev_notify(struct puffs_fctrl_io *fio, int what)
604 {
605           struct puffs_fbevent *fbevp;
606 
607  restart:
608           LIST_FOREACH(fbevp, &fio->ev_qing, pfe_entries) {
609                     if (fbevp->what & what) {
610                               fbevp->what = what;
611                               fbevp->rv = 0;
612                               LIST_REMOVE(fbevp, pfe_entries);
613                               puffs_cc_continue(fbevp->pcc);
614                               goto restart;
615                     }
616           }
617 }
618 
619 static struct puffs_framebuf *
findbuf(struct puffs_usermount * pu,struct puffs_framectrl * fctrl,struct puffs_fctrl_io * fio,struct puffs_framebuf * findme)620 findbuf(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
621           struct puffs_fctrl_io *fio, struct puffs_framebuf *findme)
622 {
623           struct puffs_framebuf *cand;
624           int notresp = 0;
625 
626           TAILQ_FOREACH(cand, &fio->res_qing, pfb_entries)
627                     if (fctrl->cmpfb(pu, findme, cand, &notresp) == 0 || notresp)
628                               break;
629 
630           assert(!(notresp && cand == NULL));
631           if (notresp || cand == NULL)
632                     return NULL;
633 
634           TAILQ_REMOVE(&fio->res_qing, cand, pfb_entries);
635           return cand;
636 }
637 
638 void
puffs__framebuf_moveinfo(struct puffs_framebuf * from,struct puffs_framebuf * to)639 puffs__framebuf_moveinfo(struct puffs_framebuf *from, struct puffs_framebuf *to)
640 {
641 
642           assert(from->istat & ISTAT_INTERNAL);
643 
644           /* migrate buffer */
645           free(to->buf);
646           to->buf = from->buf;
647 
648           /* migrate buffer info */
649           to->len = from->len;
650           to->offset = from->offset;
651           to->maxoff = from->maxoff;
652 
653           from->buf = NULL;
654           from->len = 0;
655 }
656 
657 void
puffs__framev_input(struct puffs_usermount * pu,struct puffs_framectrl * fctrl,struct puffs_fctrl_io * fio)658 puffs__framev_input(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
659           struct puffs_fctrl_io *fio)
660 {
661           struct puffs_framebuf *pufbuf, *appbuf;
662           int rv, complete;
663 
664           while ((fio->stat & FIO_DEAD) == 0 && (fio->stat & FIO_ENABLE_R)) {
665                     if ((pufbuf = fio->cur_in) == NULL) {
666                               pufbuf = puffs_framebuf_make();
667                               if (pufbuf == NULL)
668                                         return;
669                               pufbuf->istat |= ISTAT_INTERNAL;
670                               fio->cur_in = pufbuf;
671                     }
672 
673                     complete = 0;
674                     rv = fctrl->rfb(pu, pufbuf, fio->io_fd, &complete);
675 
676                     /* error */
677                     if (rv) {
678                               puffs__framev_readclose(pu, fio, rv);
679                               fio->cur_in = NULL;
680                               return;
681                     }
682 
683                     /* partial read, come back to fight another day */
684                     if (complete == 0)
685                               break;
686 
687                     /* else: full read, process */
688                     fio->cur_in = NULL;
689                     if ((pufbuf->istat & ISTAT_DIRECT) == 0) {
690                               appbuf = findbuf(pu, fctrl, fio, pufbuf);
691 
692                               /*
693                                * No request for this frame?  If fs implements
694                                * gotfb, give frame to that.  Otherwise drop it.
695                                */
696                               if (appbuf == NULL) {
697                                         if (fctrl->gotfb) {
698                                                   pufbuf->istat &= ~ISTAT_INTERNAL;
699                                                   fctrl->gotfb(pu, pufbuf);
700                                         } else {
701                                                   puffs_framebuf_destroy(pufbuf);
702                                         }
703                                         continue;
704                               }
705 
706                               puffs__framebuf_moveinfo(pufbuf, appbuf);
707                               puffs_framebuf_destroy(pufbuf);
708                     } else {
709                               appbuf = pufbuf;
710                     }
711                     appbuf->istat &= ~ISTAT_NODESTROY;
712 
713                     if (appbuf->pcc) {
714                               puffs__cc_cont(appbuf->pcc);
715                     } else if (appbuf->fcb) {
716                               appbuf->fcb(pu, appbuf, appbuf->fcb_arg, 0);
717                     } else {
718                               puffs_framebuf_destroy(appbuf);
719                     }
720 
721                     /* hopeless romantics, here we go again */
722           }
723 }
724 
725 int
puffs__framev_output(struct puffs_usermount * pu,struct puffs_framectrl * fctrl,struct puffs_fctrl_io * fio)726 puffs__framev_output(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
727           struct puffs_fctrl_io *fio)
728 {
729           struct puffs_framebuf *pufbuf;
730           int rv, complete, done;
731 
732           if (fio->stat & FIO_DEAD)
733                     return 0;
734 
735           for (pufbuf = TAILQ_FIRST(&fio->snd_qing), done = 0;
736               pufbuf && (fio->stat & FIO_DEAD) == 0 && fio->stat & FIO_ENABLE_W;
737               pufbuf = TAILQ_FIRST(&fio->snd_qing)) {
738                     complete = 0;
739                     rv = fctrl->wfb(pu, pufbuf, fio->io_fd, &complete);
740 
741                     if (rv) {
742                               puffs__framev_writeclose(pu, fio, rv);
743                               done = 1;
744                               break;
745                     }
746 
747                     /* partial write */
748                     if (complete == 0)
749                               return done;
750 
751                     /* else, complete write */
752                     TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
753 
754                     /* can't wait for result if we can't read */
755                     if (fio->stat & FIO_RDGONE) {
756                               errnotify(pu, pufbuf, ENXIO);
757                               done = 1;
758                     } else if ((pufbuf->istat & ISTAT_DIRECT)) {
759                               pufbuf->istat &= ~ISTAT_NODESTROY;
760                               done = 1;
761                               puffs__cc_cont(pufbuf->pcc);
762                     } else if ((pufbuf->istat & ISTAT_NOREPLY) == 0) {
763                               TAILQ_INSERT_TAIL(&fio->res_qing, pufbuf,
764                                   pfb_entries);
765                     } else {
766                               pufbuf->istat &= ~ISTAT_NODESTROY;
767                               puffs_framebuf_destroy(pufbuf);
768                     }
769 
770                     /* omstart! */
771           }
772 
773           return done;
774 }
775 
776 int
puffs__framev_addfd_ctrl(struct puffs_usermount * pu,int fd,int what,struct puffs_framectrl * pfctrl)777 puffs__framev_addfd_ctrl(struct puffs_usermount *pu, int fd, int what,
778           struct puffs_framectrl *pfctrl)
779 {
780           struct puffs_fctrl_io *fio;
781           struct kevent kev[2];
782           size_t nevs;
783           int rv, readenable;
784 
785           nevs = pu->pu_nevs+2;
786           if (reallocarr(&pu->pu_evs, nevs, sizeof(struct kevent)) != 0) {
787                     errno = ENOMEM;
788                     return -1;
789           }
790 
791           fio = malloc(sizeof(struct puffs_fctrl_io));
792           if (fio == NULL)
793                     return -1;
794           memset(fio, 0, sizeof(struct puffs_fctrl_io));
795           fio->io_fd = fd;
796           fio->cur_in = NULL;
797           fio->fctrl = pfctrl;
798           TAILQ_INIT(&fio->snd_qing);
799           TAILQ_INIT(&fio->res_qing);
800           LIST_INIT(&fio->ev_qing);
801 
802           readenable = 0;
803           if ((what & PUFFS_FBIO_READ) == 0)
804                     readenable = EV_DISABLE;
805 
806           if (pu->pu_state & PU_INLOOP) {
807                     struct stat st;
808                     size_t nf = 0;
809 
810                     if (fstat(fd, &st) == -1)
811                               goto out;
812                     EV_SET(&kev[nf], fd, EVFILT_READ, EV_ADD|readenable, 0, 0, fio);
813                     nf++;
814                     if (S_ISSOCK(st.st_mode)) {
815                               EV_SET(&kev[nf], fd, EVFILT_WRITE,
816                                   EV_ADD|EV_DISABLE, 0, 0, fio);
817                               nf++;
818                     }
819                     rv = kevent(pu->pu_kq, kev, nf, NULL, 0, NULL);
820                     if (rv == -1)
821                               goto out;
822           }
823           if (what & PUFFS_FBIO_READ)
824                     fio->stat |= FIO_ENABLE_R;
825           if (what & PUFFS_FBIO_WRITE)
826                     fio->stat |= FIO_ENABLE_W;
827 
828           LIST_INSERT_HEAD(&pu->pu_ios, fio, fio_entries);
829           pu->pu_nevs = nevs;
830 
831           return 0;
832 out:
833           free(fio);
834           return -1;
835 }
836 
837 int
puffs_framev_addfd(struct puffs_usermount * pu,int fd,int what)838 puffs_framev_addfd(struct puffs_usermount *pu, int fd, int what)
839 {
840 
841           return puffs__framev_addfd_ctrl(pu, fd, what,
842               &pu->pu_framectrl[PU_FRAMECTRL_USER]);
843 }
844 
845 /*
846  * XXX: the following en/disable should be coalesced and executed
847  * only during the actual kevent call.  So feel free to fix if
848  * threatened by mindblowing boredom.
849  */
850 
851 int
puffs_framev_enablefd(struct puffs_usermount * pu,int fd,int what)852 puffs_framev_enablefd(struct puffs_usermount *pu, int fd, int what)
853 {
854           struct kevent kev;
855           struct puffs_fctrl_io *fio;
856           int rv = 0;
857 
858           assert((what & (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE)) != 0);
859 
860           fio = getfiobyfd(pu, fd);
861           if (fio == NULL) {
862                     errno = ENXIO;
863                     return -1;
864           }
865 
866           /* write is enabled in the event loop if there is output */
867           if (what & PUFFS_FBIO_READ && fio->rwait == 0) {
868                     EV_SET(&kev, fd, EVFILT_READ, EV_ENABLE, 0, 0, fio);
869                     rv = kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
870           }
871 
872           if (rv == 0) {
873                     if (what & PUFFS_FBIO_READ)
874                               fio->stat |= FIO_ENABLE_R;
875                     if (what & PUFFS_FBIO_WRITE)
876                               fio->stat |= FIO_ENABLE_W;
877           }
878 
879           return rv;
880 }
881 
882 int
puffs_framev_disablefd(struct puffs_usermount * pu,int fd,int what)883 puffs_framev_disablefd(struct puffs_usermount *pu, int fd, int what)
884 {
885           struct kevent kev[2];
886           struct puffs_fctrl_io *fio;
887           size_t i;
888           int rv;
889 
890           assert((what & (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE)) != 0);
891 
892           fio = getfiobyfd(pu, fd);
893           if (fio == NULL) {
894                     errno = ENXIO;
895                     return -1;
896           }
897 
898           i = 0;
899           if (what & PUFFS_FBIO_READ && fio->rwait == 0) {
900                     EV_SET(&kev[0], fd, EVFILT_READ, EV_DISABLE, 0, 0, fio);
901                     i++;
902           }
903           if (what & PUFFS_FBIO_WRITE && fio->stat & FIO_WR && fio->wwait == 0) {
904                     EV_SET(&kev[1], fd, EVFILT_WRITE, EV_DISABLE, 0, 0, fio);
905                     i++;
906           }
907           if (i)
908                     rv = kevent(pu->pu_kq, kev, i, NULL, 0, NULL);
909           else
910                     rv = 0;
911 
912           if (rv == 0) {
913                     if (what & PUFFS_FBIO_READ)
914                               fio->stat &= ~FIO_ENABLE_R;
915                     if (what & PUFFS_FBIO_WRITE)
916                               fio->stat &= ~FIO_ENABLE_W;
917           }
918 
919           return rv;
920 }
921 
922 void
puffs__framev_readclose(struct puffs_usermount * pu,struct puffs_fctrl_io * fio,int error)923 puffs__framev_readclose(struct puffs_usermount *pu,
924           struct puffs_fctrl_io *fio, int error)
925 {
926           struct puffs_framebuf *pufbuf;
927           struct kevent kev;
928           int notflag;
929 
930           if (fio->stat & FIO_RDGONE || fio->stat & FIO_DEAD)
931                     return;
932           fio->stat |= FIO_RDGONE;
933 
934           if (fio->cur_in) {
935                     if ((fio->cur_in->istat & ISTAT_DIRECT) == 0) {
936                               puffs_framebuf_destroy(fio->cur_in);
937                               fio->cur_in = NULL;
938                     } else {
939                               errnotify(pu, fio->cur_in, error);
940                     }
941           }
942 
943           while ((pufbuf = TAILQ_FIRST(&fio->res_qing)) != NULL) {
944                     TAILQ_REMOVE(&fio->res_qing, pufbuf, pfb_entries);
945                     errnotify(pu, pufbuf, error);
946           }
947 
948           EV_SET(&kev, fio->io_fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
949           (void) kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
950 
951           notflag = PUFFS_FBIO_READ;
952           if (fio->stat & FIO_WRGONE)
953                     notflag |= PUFFS_FBIO_WRITE;
954 
955           if (fio->fctrl->fdnotfn)
956                     fio->fctrl->fdnotfn(pu, fio->io_fd, notflag);
957 }
958 
959 void
puffs__framev_writeclose(struct puffs_usermount * pu,struct puffs_fctrl_io * fio,int error)960 puffs__framev_writeclose(struct puffs_usermount *pu,
961           struct puffs_fctrl_io *fio, int error)
962 {
963           struct puffs_framebuf *pufbuf;
964           struct kevent kev;
965           int notflag;
966 
967           if (fio->stat & FIO_WRGONE || fio->stat & FIO_DEAD)
968                     return;
969           fio->stat |= FIO_WRGONE;
970 
971           while ((pufbuf = TAILQ_FIRST(&fio->snd_qing)) != NULL) {
972                     TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
973                     errnotify(pu, pufbuf, error);
974           }
975 
976           EV_SET(&kev, fio->io_fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
977           (void) kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
978 
979           notflag = PUFFS_FBIO_WRITE;
980           if (fio->stat & FIO_RDGONE)
981                     notflag |= PUFFS_FBIO_READ;
982 
983           if (fio->fctrl->fdnotfn)
984                     fio->fctrl->fdnotfn(pu, fio->io_fd, notflag);
985 }
986 
987 static int
removefio(struct puffs_usermount * pu,struct puffs_fctrl_io * fio,int error)988 removefio(struct puffs_usermount *pu, struct puffs_fctrl_io *fio, int error)
989 {
990           struct puffs_fbevent *fbevp;
991 
992           LIST_REMOVE(fio, fio_entries);
993           if (pu->pu_state & PU_INLOOP) {
994                     puffs__framev_readclose(pu, fio, error);
995                     puffs__framev_writeclose(pu, fio, error);
996           }
997 
998           while ((fbevp = LIST_FIRST(&fio->ev_qing)) != NULL) {
999                     fbevp->rv = error;
1000                     LIST_REMOVE(fbevp, pfe_entries);
1001                     puffs__goto(fbevp->pcc);
1002           }
1003 
1004           /* don't bother with realloc */
1005           pu->pu_nevs -= 2;
1006 
1007           /* don't free us yet, might have some references in event arrays */
1008           fio->stat |= FIO_DEAD;
1009           LIST_INSERT_HEAD(&pu->pu_ios_rmlist, fio, fio_entries);
1010 
1011           return 0;
1012 
1013 }
1014 
1015 int
puffs_framev_removefd(struct puffs_usermount * pu,int fd,int error)1016 puffs_framev_removefd(struct puffs_usermount *pu, int fd, int error)
1017 {
1018           struct puffs_fctrl_io *fio;
1019 
1020           fio = getfiobyfd(pu, fd);
1021           if (fio == NULL) {
1022                     errno = ENXIO;
1023                     return -1;
1024           }
1025 
1026           return removefio(pu, fio, error ? error : ECONNRESET);
1027 }
1028 
1029 void
puffs_framev_removeonclose(struct puffs_usermount * pu,int fd,int what)1030 puffs_framev_removeonclose(struct puffs_usermount *pu, int fd, int what)
1031 {
1032 
1033           if (what == (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE))
1034                     (void) puffs_framev_removefd(pu, fd, ECONNRESET);
1035 }
1036 
1037 void
puffs_framev_unmountonclose(struct puffs_usermount * pu,int fd,int what)1038 puffs_framev_unmountonclose(struct puffs_usermount *pu, int fd, int what)
1039 {
1040 
1041           /* XXX & X: unmount is non-sensible */
1042           puffs_framev_removeonclose(pu, fd, what);
1043           if (what == (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE))
1044                     PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTED);
1045 }
1046 
1047 void
puffs_framev_init(struct puffs_usermount * pu,puffs_framev_readframe_fn rfb,puffs_framev_writeframe_fn wfb,puffs_framev_cmpframe_fn cmpfb,puffs_framev_gotframe_fn gotfb,puffs_framev_fdnotify_fn fdnotfn)1048 puffs_framev_init(struct puffs_usermount *pu,
1049           puffs_framev_readframe_fn rfb, puffs_framev_writeframe_fn wfb,
1050           puffs_framev_cmpframe_fn cmpfb, puffs_framev_gotframe_fn gotfb,
1051           puffs_framev_fdnotify_fn fdnotfn)
1052 {
1053           struct puffs_framectrl *pfctrl;
1054 
1055           pfctrl = &pu->pu_framectrl[PU_FRAMECTRL_USER];
1056           pfctrl->rfb = rfb;
1057           pfctrl->wfb = wfb;
1058           pfctrl->cmpfb = cmpfb;
1059           pfctrl->gotfb = gotfb;
1060           pfctrl->fdnotfn = fdnotfn;
1061 }
1062 
1063 void
puffs__framev_exit(struct puffs_usermount * pu)1064 puffs__framev_exit(struct puffs_usermount *pu)
1065 {
1066           struct puffs_fctrl_io *fio;
1067 
1068           while ((fio = LIST_FIRST(&pu->pu_ios)) != NULL)
1069                     removefio(pu, fio, ENXIO);
1070           free(pu->pu_evs);
1071 
1072           /* closing pu->pu_kq takes care of puffsfd */
1073 }
1074