puffs.c revision 1.62.2.3 1 /* $NetBSD: puffs.c,v 1.62.2.3 2008/01/09 01:36:48 matt Exp $ */
2
3 /*
4 * Copyright (c) 2005, 2006, 2007 Antti Kantee. All Rights Reserved.
5 *
6 * Development of this software was supported by the
7 * Google Summer of Code program and the Ulla Tuominen Foundation.
8 * The Google SoC project was mentored by Bill Studenmund.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #if !defined(lint)
34 __RCSID("$NetBSD: puffs.c,v 1.62.2.3 2008/01/09 01:36:48 matt Exp $");
35 #endif /* !lint */
36
37 #include <sys/param.h>
38 #include <sys/mount.h>
39
40 #include <assert.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <mntopts.h>
45 #include <paths.h>
46 #include <puffs.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <syslog.h>
51 #include <unistd.h>
52
53 #include "puffs_priv.h"
54
55 /* Most file systems want this for opts, so just give it to them */
56 const struct mntopt puffsmopts[] = {
57 MOPT_STDOPTS,
58 PUFFSMOPT_STD,
59 MOPT_NULL,
60 };
61
62 #ifdef PUFFS_WITH_THREADS
63 #include <pthread.h>
64 pthread_mutex_t pu_lock = PTHREAD_MUTEX_INITIALIZER;
65 #endif
66
67 #define FILLOP(lower, upper) \
68 do { \
69 if (pops->puffs_node_##lower) \
70 opmask[PUFFS_VN_##upper] = 1; \
71 } while (/*CONSTCOND*/0)
72 static void
73 fillvnopmask(struct puffs_ops *pops, uint8_t *opmask)
74 {
75
76 memset(opmask, 0, PUFFS_VN_MAX);
77
78 FILLOP(create, CREATE);
79 FILLOP(mknod, MKNOD);
80 FILLOP(open, OPEN);
81 FILLOP(close, CLOSE);
82 FILLOP(access, ACCESS);
83 FILLOP(getattr, GETATTR);
84 FILLOP(setattr, SETATTR);
85 FILLOP(poll, POLL); /* XXX: not ready in kernel */
86 FILLOP(mmap, MMAP);
87 FILLOP(fsync, FSYNC);
88 FILLOP(seek, SEEK);
89 FILLOP(remove, REMOVE);
90 FILLOP(link, LINK);
91 FILLOP(rename, RENAME);
92 FILLOP(mkdir, MKDIR);
93 FILLOP(rmdir, RMDIR);
94 FILLOP(symlink, SYMLINK);
95 FILLOP(readdir, READDIR);
96 FILLOP(readlink, READLINK);
97 FILLOP(reclaim, RECLAIM);
98 FILLOP(inactive, INACTIVE);
99 FILLOP(print, PRINT);
100 FILLOP(read, READ);
101 FILLOP(write, WRITE);
102 }
103 #undef FILLOP
104
105 /*
106 * Go over all framev entries and write everything we can. This is
107 * mostly for the benefit of delivering "unmount" to the kernel.
108 */
109 static void
110 finalpush(struct puffs_usermount *pu)
111 {
112 struct puffs_fctrl_io *fio;
113
114 LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
115 if (fio->stat & FIO_WRGONE)
116 continue;
117
118 puffs_framev_output(pu, fio->fctrl, fio);
119 }
120 }
121
122 /*ARGSUSED*/
123 static void
124 puffs_defaulterror(struct puffs_usermount *pu, uint8_t type,
125 int error, const char *str, void *cookie)
126 {
127
128 fprintf(stderr, "abort: type %d, error %d, cookie %p (%s)\n",
129 type, error, cookie, str);
130 abort();
131 }
132
133 int
134 puffs_getselectable(struct puffs_usermount *pu)
135 {
136
137 return pu->pu_fd;
138 }
139
140 uint64_t
141 puffs__nextreq(struct puffs_usermount *pu)
142 {
143 uint64_t rv;
144
145 PU_LOCK();
146 rv = pu->pu_nextreq++;
147 PU_UNLOCK();
148
149 return rv;
150 }
151
152 int
153 puffs_setblockingmode(struct puffs_usermount *pu, int mode)
154 {
155 int rv, x;
156
157 assert(puffs_getstate(pu) == PUFFS_STATE_RUNNING);
158
159 if (mode != PUFFSDEV_BLOCK && mode != PUFFSDEV_NONBLOCK) {
160 errno = EINVAL;
161 return -1;
162 }
163
164 x = mode;
165 rv = ioctl(pu->pu_fd, FIONBIO, &x);
166
167 if (rv == 0) {
168 if (mode == PUFFSDEV_BLOCK)
169 pu->pu_state &= ~PU_ASYNCFD;
170 else
171 pu->pu_state |= PU_ASYNCFD;
172 }
173
174 return rv;
175 }
176
177 int
178 puffs_getstate(struct puffs_usermount *pu)
179 {
180
181 return pu->pu_state & PU_STATEMASK;
182 }
183
184 void
185 puffs_setstacksize(struct puffs_usermount *pu, size_t ss)
186 {
187 long psize;
188 int stackshift;
189
190 psize = sysconf(_SC_PAGESIZE);
191 if (ss < 2*psize) {
192 ss = 2*psize;
193 fprintf(stderr, "puffs_setstacksize: adjusting stacksize "
194 "to minimum %zu\n", ss);
195 }
196
197 assert(puffs_getstate(pu) == PUFFS_STATE_BEFOREMOUNT);
198 stackshift = -1;
199 while (ss) {
200 ss >>= 1;
201 stackshift++;
202 }
203 pu->pu_cc_stackshift = stackshift;
204 }
205
206 struct puffs_pathobj *
207 puffs_getrootpathobj(struct puffs_usermount *pu)
208 {
209 struct puffs_node *pnr;
210
211 pnr = pu->pu_pn_root;
212 if (pnr == NULL) {
213 errno = ENOENT;
214 return NULL;
215 }
216
217 return &pnr->pn_po;
218 }
219
220 void
221 puffs_setroot(struct puffs_usermount *pu, struct puffs_node *pn)
222 {
223
224 pu->pu_pn_root = pn;
225 }
226
227 struct puffs_node *
228 puffs_getroot(struct puffs_usermount *pu)
229 {
230
231 return pu->pu_pn_root;
232 }
233
234 void
235 puffs_setrootinfo(struct puffs_usermount *pu, enum vtype vt,
236 vsize_t vsize, dev_t rdev)
237 {
238 struct puffs_kargs *pargs = pu->pu_kargp;
239
240 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT) {
241 warnx("puffs_setrootinfo: call has effect only "
242 "before mount\n");
243 return;
244 }
245
246 pargs->pa_root_vtype = vt;
247 pargs->pa_root_vsize = vsize;
248 pargs->pa_root_rdev = rdev;
249 }
250
251 void *
252 puffs_getspecific(struct puffs_usermount *pu)
253 {
254
255 return pu->pu_privdata;
256 }
257
258 size_t
259 puffs_getmaxreqlen(struct puffs_usermount *pu)
260 {
261
262 return pu->pu_maxreqlen;
263 }
264
265 void
266 puffs_setmaxreqlen(struct puffs_usermount *pu, size_t reqlen)
267 {
268
269 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT)
270 warnx("puffs_setmaxreqlen: call has effect only "
271 "before mount\n");
272
273 pu->pu_kargp->pa_maxmsglen = reqlen;
274 }
275
276 void
277 puffs_setfhsize(struct puffs_usermount *pu, size_t fhsize, int flags)
278 {
279
280 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT)
281 warnx("puffs_setfhsize: call has effect only before mount\n");
282
283 pu->pu_kargp->pa_fhsize = fhsize;
284 pu->pu_kargp->pa_fhflags = flags;
285 }
286
287 void
288 puffs_setncookiehash(struct puffs_usermount *pu, int nhash)
289 {
290
291 if (puffs_getstate(pu) != PUFFS_STATE_BEFOREMOUNT)
292 warnx("puffs_setfhsize: call has effect only before mount\n");
293
294 pu->pu_kargp->pa_nhashbuckets = nhash;
295 }
296
297 void
298 puffs_set_pathbuild(struct puffs_usermount *pu, pu_pathbuild_fn fn)
299 {
300
301 pu->pu_pathbuild = fn;
302 }
303
304 void
305 puffs_set_pathtransform(struct puffs_usermount *pu, pu_pathtransform_fn fn)
306 {
307
308 pu->pu_pathtransform = fn;
309 }
310
311 void
312 puffs_set_pathcmp(struct puffs_usermount *pu, pu_pathcmp_fn fn)
313 {
314
315 pu->pu_pathcmp = fn;
316 }
317
318 void
319 puffs_set_pathfree(struct puffs_usermount *pu, pu_pathfree_fn fn)
320 {
321
322 pu->pu_pathfree = fn;
323 }
324
325 void
326 puffs_set_namemod(struct puffs_usermount *pu, pu_namemod_fn fn)
327 {
328
329 pu->pu_namemod = fn;
330 }
331
332 void
333 puffs_set_errnotify(struct puffs_usermount *pu, pu_errnotify_fn fn)
334 {
335
336 pu->pu_errnotify = fn;
337 }
338
339 void
340 puffs_set_cmap(struct puffs_usermount *pu, pu_cmap_fn fn)
341 {
342
343 pu->pu_cmap = fn;
344 }
345
346 void
347 puffs_ml_setloopfn(struct puffs_usermount *pu, puffs_ml_loop_fn lfn)
348 {
349
350 pu->pu_ml_lfn = lfn;
351 }
352
353 void
354 puffs_ml_settimeout(struct puffs_usermount *pu, struct timespec *ts)
355 {
356
357 if (ts == NULL) {
358 pu->pu_ml_timep = NULL;
359 } else {
360 pu->pu_ml_timeout = *ts;
361 pu->pu_ml_timep = &pu->pu_ml_timeout;
362 }
363 }
364
365 void
366 puffs_set_prepost(struct puffs_usermount *pu,
367 pu_prepost_fn pre, pu_prepost_fn pst)
368 {
369
370 pu->pu_oppre = pre;
371 pu->pu_oppost = pst;
372 }
373
374 void
375 puffs_setback(struct puffs_cc *pcc, int whatback)
376 {
377 struct puffs_req *preq = puffs__framebuf_getdataptr(pcc->pcc_pb);
378
379 assert(PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN && (
380 preq->preq_optype == PUFFS_VN_OPEN ||
381 preq->preq_optype == PUFFS_VN_MMAP ||
382 preq->preq_optype == PUFFS_VN_REMOVE ||
383 preq->preq_optype == PUFFS_VN_RMDIR ||
384 preq->preq_optype == PUFFS_VN_INACTIVE));
385
386 preq->preq_setbacks |= whatback & PUFFS_SETBACK_MASK;
387 }
388
389 int
390 puffs_daemon(struct puffs_usermount *pu, int nochdir, int noclose)
391 {
392 ssize_t n;
393 int parent, value, fd;
394
395 if (pipe(pu->pu_dpipe) == -1)
396 return -1;
397
398 switch (fork()) {
399 case -1:
400 return -1;
401 case 0:
402 parent = 0;
403 break;
404 default:
405 parent = 1;
406 break;
407 }
408 pu->pu_state |= PU_PUFFSDAEMON;
409
410 if (parent) {
411 n = read(pu->pu_dpipe[0], &value, sizeof(int));
412 if (n == -1)
413 err(1, "puffs_daemon");
414 assert(n == sizeof(value));
415 if (value) {
416 errno = value;
417 err(1, "puffs_daemon");
418 }
419 exit(0);
420 } else {
421 if (setsid() == -1)
422 goto fail;
423
424 if (!nochdir)
425 chdir("/");
426
427 if (!noclose) {
428 fd = open(_PATH_DEVNULL, O_RDWR, 0);
429 if (fd == -1)
430 goto fail;
431 dup2(fd, STDIN_FILENO);
432 dup2(fd, STDOUT_FILENO);
433 dup2(fd, STDERR_FILENO);
434 if (fd > STDERR_FILENO)
435 close(fd);
436 }
437 return 0;
438 }
439
440 fail:
441 n = write(pu->pu_dpipe[1], &errno, sizeof(int));
442 assert(n == 4);
443 return -1;
444 }
445
446 int
447 puffs_mount(struct puffs_usermount *pu, const char *dir, int mntflags,
448 void *cookie)
449 {
450 char rp[MAXPATHLEN];
451 ssize_t n;
452 int rv, fd, sverrno;
453 char *comfd;
454
455 pu->pu_kargp->pa_root_cookie = cookie;
456
457 /* XXXkludgehere */
458 /* kauth doesn't provide this service any longer */
459 if (geteuid() != 0)
460 mntflags |= MNT_NOSUID | MNT_NODEV;
461
462 if (realpath(dir, rp) == NULL) {
463 rv = -1;
464 goto out;
465 }
466
467 if (strcmp(dir, rp) != 0) {
468 warnx("puffs_mount: \"%s\" is a relative path.", dir);
469 warnx("puffs_mount: using \"%s\" instead.", rp);
470 }
471
472 /*
473 * Undocumented... Well, documented only here.
474 *
475 * This is used for imaginative purposes. If the env variable is
476 * set, puffs_mount() doesn't do the regular mount procedure.
477 * Rather, it crams the mount data down the comfd and sets comfd as
478 * the puffs descriptor.
479 *
480 * This shouldn't be used unless you can read my mind ( ... or write
481 * it, not to mention execute it, but that's starting to get silly).
482 */
483 if ((comfd = getenv("PUFFS_COMFD")) != NULL) {
484 size_t len;
485
486 if (sscanf(comfd, "%d", &pu->pu_fd) != 1) {
487 errno = EINVAL;
488 rv = -1;
489 goto out;
490 }
491 /* check that what we got at least resembles an fd */
492 if (fcntl(pu->pu_fd, F_GETFL) == -1) {
493 rv = -1;
494 goto out;
495 }
496
497 len = strlen(dir)+1;
498
499 #define allwrite(buf, len) \
500 do { \
501 ssize_t al_rv; \
502 al_rv = write(pu->pu_fd, buf, len); \
503 if (al_rv != len) { \
504 if (al_rv != -1) \
505 errno = EIO; \
506 rv = -1; \
507 abort();\
508 goto out; \
509 } \
510 } while (/*CONSTCOND*/0)
511 allwrite(&len, sizeof(len));
512 allwrite(dir, len);
513 len = strlen(pu->pu_kargp->pa_mntfromname)+1;
514 allwrite(&len, sizeof(len));
515 allwrite(pu->pu_kargp->pa_mntfromname, len);
516 allwrite(&mntflags, sizeof(mntflags));
517 allwrite(pu->pu_kargp, sizeof(*pu->pu_kargp));
518 allwrite(&pu->pu_flags, sizeof(pu->pu_flags));
519 #undef allwrite
520
521 rv = 0;
522 } else {
523 fd = open(_PATH_PUFFS, O_RDWR);
524 if (fd == -1) {
525 warnx("puffs_mount: cannot open %s", _PATH_PUFFS);
526 rv = -1;
527 goto out;
528 }
529 if (fd <= 2)
530 warnx("puffs_mount: device fd %d (<= 2), sure this is "
531 "what you want?", fd);
532
533 pu->pu_kargp->pa_fd = pu->pu_fd = fd;
534 if ((rv = mount(MOUNT_PUFFS, rp, mntflags,
535 pu->pu_kargp, sizeof(struct puffs_kargs))) == -1)
536 goto out;
537 }
538
539 PU_SETSTATE(pu, PUFFS_STATE_RUNNING);
540
541 out:
542 if (rv != 0)
543 sverrno = errno;
544 else
545 sverrno = 0;
546 free(pu->pu_kargp);
547 pu->pu_kargp = NULL;
548
549 if (pu->pu_state & PU_PUFFSDAEMON) {
550 n = write(pu->pu_dpipe[1], &sverrno, sizeof(int));
551 assert(n == 4);
552 close(pu->pu_dpipe[0]);
553 close(pu->pu_dpipe[1]);
554 }
555
556 errno = sverrno;
557 return rv;
558 }
559
560 struct puffs_usermount *
561 _puffs_init(int develv, struct puffs_ops *pops, const char *mntfromname,
562 const char *puffsname, void *priv, uint32_t pflags)
563 {
564 struct puffs_usermount *pu;
565 struct puffs_kargs *pargs;
566 int sverrno;
567
568 if (develv != PUFFS_DEVEL_LIBVERSION) {
569 warnx("puffs_init: mounting with lib version %d, need %d",
570 develv, PUFFS_DEVEL_LIBVERSION);
571 errno = EINVAL;
572 return NULL;
573 }
574
575 pu = malloc(sizeof(struct puffs_usermount));
576 if (pu == NULL)
577 goto failfree;
578 memset(pu, 0, sizeof(struct puffs_usermount));
579
580 pargs = pu->pu_kargp = malloc(sizeof(struct puffs_kargs));
581 if (pargs == NULL)
582 goto failfree;
583 memset(pargs, 0, sizeof(struct puffs_kargs));
584
585 pargs->pa_vers = PUFFSDEVELVERS | PUFFSVERSION;
586 pargs->pa_flags = PUFFS_FLAG_KERN(pflags);
587 fillvnopmask(pops, pargs->pa_vnopmask);
588 (void)strlcpy(pargs->pa_typename, puffsname,
589 sizeof(pargs->pa_typename));
590 (void)strlcpy(pargs->pa_mntfromname, mntfromname,
591 sizeof(pargs->pa_mntfromname));
592
593 puffs_zerostatvfs(&pargs->pa_svfsb);
594 pargs->pa_root_cookie = NULL;
595 pargs->pa_root_vtype = VDIR;
596 pargs->pa_root_vsize = 0;
597 pargs->pa_root_rdev = 0;
598 pargs->pa_maxmsglen = 0;
599
600 pu->pu_flags = pflags;
601 pu->pu_ops = *pops;
602 free(pops); /* XXX */
603
604 pu->pu_privdata = priv;
605 pu->pu_cc_stackshift = PUFFS_CC_STACKSHIFT_DEFAULT;
606 LIST_INIT(&pu->pu_pnodelst);
607 LIST_INIT(&pu->pu_ios);
608 LIST_INIT(&pu->pu_ios_rmlist);
609 LIST_INIT(&pu->pu_ccnukelst);
610 TAILQ_INIT(&pu->pu_sched);
611 TAILQ_INIT(&pu->pu_exq);
612
613 pu->pu_framectrl[PU_FRAMECTRL_FS].rfb = puffs_fsframe_read;
614 pu->pu_framectrl[PU_FRAMECTRL_FS].wfb = puffs_fsframe_write;
615 pu->pu_framectrl[PU_FRAMECTRL_FS].cmpfb = puffs_fsframe_cmp;
616 pu->pu_framectrl[PU_FRAMECTRL_FS].gotfb = puffs_fsframe_gotframe;
617 pu->pu_framectrl[PU_FRAMECTRL_FS].fdnotfn = puffs_framev_unmountonclose;
618
619 /* defaults for some user-settable translation functions */
620 pu->pu_cmap = NULL; /* identity translation */
621
622 pu->pu_pathbuild = puffs_stdpath_buildpath;
623 pu->pu_pathfree = puffs_stdpath_freepath;
624 pu->pu_pathcmp = puffs_stdpath_cmppath;
625 pu->pu_pathtransform = NULL;
626 pu->pu_namemod = NULL;
627
628 pu->pu_errnotify = puffs_defaulterror;
629
630 PU_SETSTATE(pu, PUFFS_STATE_BEFOREMOUNT);
631
632 return pu;
633
634 failfree:
635 /* can't unmount() from here for obvious reasons */
636 sverrno = errno;
637 free(pu);
638 errno = sverrno;
639 return NULL;
640 }
641
642 /*
643 * XXX: there's currently no clean way to request unmount from
644 * within the user server, so be very brutal about it.
645 */
646 /*ARGSUSED1*/
647 int
648 puffs_exit(struct puffs_usermount *pu, int force)
649 {
650 struct puffs_node *pn;
651
652 force = 1; /* currently */
653
654 if (pu->pu_fd)
655 close(pu->pu_fd);
656
657 while ((pn = LIST_FIRST(&pu->pu_pnodelst)) != NULL)
658 puffs_pn_put(pn);
659
660 finalpush(pu);
661 puffs_framev_exit(pu);
662 if (pu->pu_state & PU_HASKQ)
663 close(pu->pu_kq);
664 free(pu);
665
666 return 0; /* always succesful for now, WILL CHANGE */
667 }
668
669 int
670 puffs_mainloop(struct puffs_usermount *pu)
671 {
672 struct puffs_framectrl *pfctrl;
673 struct puffs_fctrl_io *fio;
674 struct puffs_cc *pcc;
675 struct kevent *curev;
676 size_t nchanges;
677 int ndone, sverrno;
678
679 assert(puffs_getstate(pu) >= PUFFS_STATE_RUNNING);
680
681 pu->pu_kq = kqueue();
682 if (pu->pu_kq == -1)
683 goto out;
684 pu->pu_state |= PU_HASKQ;
685
686 puffs_setblockingmode(pu, PUFFSDEV_NONBLOCK);
687 if (puffs__framev_addfd_ctrl(pu, puffs_getselectable(pu),
688 PUFFS_FBIO_READ | PUFFS_FBIO_WRITE,
689 &pu->pu_framectrl[PU_FRAMECTRL_FS]) == -1)
690 goto out;
691
692 curev = realloc(pu->pu_evs, (2*pu->pu_nfds)*sizeof(struct kevent));
693 if (curev == NULL)
694 goto out;
695 pu->pu_evs = curev;
696
697 LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
698 EV_SET(curev, fio->io_fd, EVFILT_READ, EV_ADD,
699 0, 0, (uintptr_t)fio);
700 curev++;
701 EV_SET(curev, fio->io_fd, EVFILT_WRITE, EV_ADD | EV_DISABLE,
702 0, 0, (uintptr_t)fio);
703 curev++;
704 }
705 if (kevent(pu->pu_kq, pu->pu_evs, 2*pu->pu_nfds, NULL, 0, NULL) == -1)
706 goto out;
707
708 pu->pu_state |= PU_INLOOP;
709
710 while (puffs_getstate(pu) != PUFFS_STATE_UNMOUNTED) {
711 if (pu->pu_ml_lfn)
712 pu->pu_ml_lfn(pu);
713
714 /* XXX: can we still do these optimizations? */
715 #if 0
716 /*
717 * Do this here, because:
718 * a) loopfunc might generate some results
719 * b) it's still "after" event handling (except for round 1)
720 */
721 if (puffs_req_putput(ppr) == -1)
722 goto out;
723 puffs_req_resetput(ppr);
724
725 /* micro optimization: skip kevent syscall if possible */
726 if (pu->pu_nfds == 1 && pu->pu_ml_timep == NULL
727 && (pu->pu_state & PU_ASYNCFD) == 0) {
728 pfctrl = XXX->fctrl;
729 puffs_framev_input(pu, pfctrl, XXX);
730 continue;
731 }
732 #endif
733
734 /* else: do full processing */
735 /* Don't bother worrying about O(n) for now */
736 LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
737 if (fio->stat & FIO_WRGONE)
738 continue;
739
740 pfctrl = fio->fctrl;
741
742 /*
743 * Try to write out everything to avoid the
744 * need for enabling EVFILT_WRITE. The likely
745 * case is that we can fit everything into the
746 * socket buffer.
747 */
748 puffs_framev_output(pu, pfctrl, fio);
749 }
750
751 /*
752 * Build list of which to enable/disable in writecheck.
753 */
754 nchanges = 0;
755 LIST_FOREACH(fio, &pu->pu_ios, fio_entries) {
756 if (fio->stat & FIO_WRGONE)
757 continue;
758
759 /* en/disable write checks for kqueue as needed */
760 assert((FIO_EN_WRITE(fio) && FIO_RM_WRITE(fio)) == 0);
761 if (FIO_EN_WRITE(fio)) {
762 EV_SET(&pu->pu_evs[nchanges], fio->io_fd,
763 EVFILT_WRITE, EV_ENABLE, 0, 0,
764 (uintptr_t)fio);
765 fio->stat |= FIO_WR;
766 nchanges++;
767 }
768 if (FIO_RM_WRITE(fio)) {
769 EV_SET(&pu->pu_evs[nchanges], fio->io_fd,
770 EVFILT_WRITE, EV_DISABLE, 0, 0,
771 (uintptr_t)fio);
772 fio->stat &= ~FIO_WR;
773 nchanges++;
774 }
775 assert(nchanges <= pu->pu_nfds);
776 }
777
778 ndone = kevent(pu->pu_kq, pu->pu_evs, nchanges,
779 pu->pu_evs, 2*pu->pu_nfds, pu->pu_ml_timep);
780
781 if (ndone == -1) {
782 if (errno != EINTR)
783 goto out;
784 else
785 continue;
786 }
787
788 /* uoptimize */
789 if (ndone == 0)
790 continue;
791
792 /* iterate over the results */
793 for (curev = pu->pu_evs; ndone--; curev++) {
794 int what;
795
796 #if 0
797 /* get & possibly dispatch events from kernel */
798 if (curev->ident == puffsfd) {
799 if (puffs_req_handle(pgr, ppr, 0) == -1)
800 goto out;
801 continue;
802 }
803 #endif
804
805 fio = (void *)curev->udata;
806 pfctrl = fio->fctrl;
807 if (curev->flags & EV_ERROR) {
808 assert(curev->filter == EVFILT_WRITE);
809 fio->stat &= ~FIO_WR;
810
811 /* XXX: how to know if it's a transient error */
812 puffs_framev_writeclose(pu, fio,
813 (int)curev->data);
814 puffs_framev_notify(fio, PUFFS_FBIO_ERROR);
815 continue;
816 }
817
818 what = 0;
819 if (curev->filter == EVFILT_READ) {
820 puffs_framev_input(pu, pfctrl, fio);
821 what |= PUFFS_FBIO_READ;
822 }
823
824 else if (curev->filter == EVFILT_WRITE) {
825 puffs_framev_output(pu, pfctrl, fio);
826 what |= PUFFS_FBIO_WRITE;
827 }
828 if (what)
829 puffs_framev_notify(fio, what);
830 }
831
832 /*
833 * Schedule continuations.
834 */
835 while ((pcc = TAILQ_FIRST(&pu->pu_sched)) != NULL) {
836 TAILQ_REMOVE(&pu->pu_sched, pcc, entries);
837 puffs_goto(pcc);
838 }
839
840 /*
841 * Really free fd's now that we don't have references
842 * to them.
843 */
844 while ((fio = LIST_FIRST(&pu->pu_ios_rmlist)) != NULL) {
845 LIST_REMOVE(fio, fio_entries);
846 free(fio);
847 }
848 }
849 finalpush(pu);
850 errno = 0;
851
852 out:
853 /* store the real error for a while */
854 sverrno = errno;
855
856 errno = sverrno;
857 if (errno)
858 return -1;
859 else
860 return 0;
861 }
862