msg.c revision 1.9 1 /* $NetBSD: msg.c,v 1.9 2010/10/11 05:37:58 manu Exp $ */
2
3 /*-
4 * Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <err.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <sysexits.h>
35 #include <syslog.h>
36 #include <paths.h>
37 #include <puffs.h>
38 #include <limits.h>
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <sys/un.h>
42 #include <machine/vmparam.h>
43
44 #include "../../lib/libperfuse/perfuse_if.h"
45 #include "perfused.h"
46
47 static int xchg_pb_inloop(struct puffs_usermount *a, struct puffs_framebuf *,
48 int, enum perfuse_xchg_pb_reply);
49 static int xchg_pb_early(struct puffs_usermount *a, struct puffs_framebuf *,
50 int, enum perfuse_xchg_pb_reply);
51
52 int
53 perfuse_open_sock(void)
54 {
55 int s;
56 struct sockaddr_un sun;
57 const struct sockaddr *sa;
58 uint32_t opt;
59
60 (void)unlink(_PATH_FUSE);
61
62 if ((s = socket(AF_LOCAL, SOCK_DGRAM, 0)) == -1)
63 err(EX_OSERR, "socket failed");
64
65 sa = (const struct sockaddr *)(void *)&sun;
66 sun.sun_len = sizeof(sun);
67 sun.sun_family = AF_LOCAL;
68 (void)strcpy(sun.sun_path, _PATH_FUSE);
69
70 /*
71 * Set a buffer lentgh large enough so that a few FUSE packets
72 * will fit.
73 * XXX We will have to find how many packets we need
74 */
75 opt = 4 * FUSE_BUFSIZE;
76 if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) != 0)
77 DWARN("%s: setsockopt SO_SNDBUF to %d failed", __func__, opt);
78
79 opt = 4 * FUSE_BUFSIZE;
80 if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) != 0)
81 DWARN("%s: setsockopt SO_RCVBUF to %d failed", __func__, opt);
82
83 /*
84 * Request peer credentials
85 */
86 opt = 1;
87 if (setsockopt(s, 0, LOCAL_CREDS, &opt, sizeof(opt)) != 0)
88 DWARN("%s: setsockopt LOCAL_CREDS failed", __func__);
89
90 if (bind(s, sa, (socklen_t )sun.sun_len) == -1)
91 err(EX_OSERR, "cannot open \"%s\" socket", _PATH_FUSE);
92
93 if (connect(s, sa, (socklen_t )sun.sun_len) == -1)
94 err(EX_OSERR, "cannot open \"%s\" socket", _PATH_FUSE);
95
96 return s;
97 }
98
99
100 void *
101 perfuse_recv_early(fd, sockcred, sockcred_len)
102 int fd;
103 struct sockcred *sockcred;
104 size_t sockcred_len;
105 {
106 struct fuse_out_header foh;
107 size_t len;
108 char *buf;
109 struct msghdr msg;
110 char cmsg_buf[sizeof(struct cmsghdr) + SOCKCREDSIZE(NGROUPS_MAX)];
111 struct cmsghdr *cmsg = (struct cmsghdr *)(void *)&cmsg_buf;
112 struct sockcred *sc = (struct sockcred *)(void *)(cmsg + 1);
113 struct iovec iov;
114
115 len = sizeof(foh);
116
117 /*
118 * We use the complicated recvmsg because we want peer creds.
119 */
120 iov.iov_base = &foh;
121 iov.iov_len = len;
122 msg.msg_name = NULL;
123 msg.msg_namelen = 0;
124 msg.msg_iov = &iov;
125 msg.msg_iovlen = 1;
126 msg.msg_control = cmsg;
127 msg.msg_controllen = sizeof(cmsg_buf);
128 msg.msg_flags = 0;
129
130 if (recvmsg(fd, &msg, MSG_NOSIGNAL|MSG_PEEK) != (ssize_t)len) {
131 DWARN("short recv (header)");
132 return NULL;
133 }
134
135 if (cmsg->cmsg_type != SCM_CREDS) {
136 DWARNX("No SCM_CREDS");
137 return NULL;
138 }
139
140 if (sockcred != NULL)
141 (void)memcpy(sockcred, sc,
142 MIN(cmsg->cmsg_len - sizeof(*cmsg), sockcred_len));
143
144
145 len = foh.len;
146 if ((buf = malloc(len)) == NULL)
147 err(EX_OSERR, "malloc(%zd) failed", len);
148
149 if (recv(fd, buf, len, MSG_NOSIGNAL) != (ssize_t)len) {
150 DWARN("short recv (frame)");
151 return NULL;
152 }
153
154 return buf;
155 }
156
157
158 perfuse_msg_t *
159 perfuse_new_pb (pu, opc, opcode, payload_len, cred)
160 struct puffs_usermount *pu;
161 puffs_cookie_t opc;
162 int opcode;
163 size_t payload_len;
164 const struct puffs_cred *cred;
165 {
166 struct puffs_framebuf *pb;
167 struct fuse_in_header *fih;
168 struct puffs_cc *pcc;
169 uint64_t nodeid;
170 void *data;
171 size_t len;
172
173 if ((pb = puffs_framebuf_make()) == NULL)
174 DERR(EX_OSERR, "puffs_framebuf_make failed");
175
176 len = payload_len + sizeof(*fih);
177 nodeid = (opc != 0) ? perfuse_get_ino(pu, opc) : PERFUSE_UNKNOWN_INO;
178
179 if (puffs_framebuf_reserve_space(pb, len) != 0)
180 DERR(EX_OSERR, "puffs_framebuf_reserve_space failed");
181
182 if (puffs_framebuf_getwindow(pb, 0, &data, &len) != 0)
183 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
184 if (len != payload_len + sizeof(*fih))
185 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short len");
186
187 (void)memset(data, 0, len);
188 fih = (struct fuse_in_header *)data;
189 fih->len = (uint32_t)len;
190 fih->opcode = opcode;
191 fih->unique = perfuse_next_unique(pu);
192 fih->nodeid = nodeid;
193 fih->uid = (uid_t)-1;
194 fih->gid = (gid_t)-1;
195 fih->pid = 0;
196 if (cred != NULL) {
197 (void)puffs_cred_getuid(cred, &fih->uid);
198 (void)puffs_cred_getgid(cred, &fih->gid);
199 }
200 if ((pcc = puffs_cc_getcc(pu)) != NULL)
201 (void)puffs_cc_getcaller(pcc, (pid_t *)&fih->pid, NULL);
202
203 return (perfuse_msg_t *)(void *)pb;
204 }
205
206 /*
207 * framebuf send/receive primitives based on pcc are
208 * not available until puffs mainloop is entered.
209 * This xchg_pb_inloop() variant allow early communication.
210 */
211 static int
212 xchg_pb_early(pu, pb, fd, reply)
213 struct puffs_usermount *pu;
214 struct puffs_framebuf *pb;
215 int fd;
216 enum perfuse_xchg_pb_reply reply;
217 {
218 int done;
219 int error;
220
221 done = 0;
222 while (done == 0) {
223 if ((error = perfuse_writeframe(pu, pb, fd, &done)) != 0)
224 return error;
225 }
226
227 if (reply == no_reply) {
228 puffs_framebuf_destroy(pb);
229 return 0;
230 } else {
231 puffs_framebuf_recycle(pb);
232 }
233
234 done = 0;
235 while (done == 0) {
236 if ((error = perfuse_readframe(pu, pb, fd, &done)) != 0)
237 return error;
238 }
239
240 return 0;
241 }
242
243 static int
244 xchg_pb_inloop(pu, pb, fd, reply)
245 struct puffs_usermount *pu;
246 struct puffs_framebuf *pb;
247 int fd;
248 enum perfuse_xchg_pb_reply reply;
249 {
250 struct puffs_cc *pcc;
251 int error;
252
253 if (reply == no_reply) {
254 error = puffs_framev_enqueue_justsend(pu, fd, pb, 0, 0);
255 } else {
256 pcc = puffs_cc_getcc(pu);
257 error = puffs_framev_enqueue_cc(pcc, fd, pb, 0);
258 }
259
260 return error;
261 }
262
263 int
264 perfuse_xchg_pb(pu, pm, expected_len, reply)
265 struct puffs_usermount *pu;
266 perfuse_msg_t *pm;
267 size_t expected_len;
268 enum perfuse_xchg_pb_reply reply;
269 {
270 struct puffs_framebuf *pb = (struct puffs_framebuf *)(void *)pm;
271 int fd;
272 int error;
273 struct fuse_out_header *foh;
274 #ifdef PERFUSE_DEBUG
275 struct fuse_in_header *fih;
276 uint64_t nodeid;
277 int opcode;
278 uint64_t unique_in;
279 uint64_t unique_out;
280
281 fih = perfuse_get_inhdr(pm);
282 unique_in = fih->unique;
283 nodeid = fih->nodeid;
284 opcode = fih->opcode;
285
286 if (perfuse_diagflags & PDF_FUSE)
287 DPRINTF("> unique = %"PRId64", nodeid = %"PRId64", "
288 "opcode = %s (%d)\n",
289 unique_in, nodeid, perfuse_opname(opcode), opcode);
290
291 if (perfuse_diagflags & PDF_DUMP)
292 perfuse_hexdump((char *)fih, fih->len);
293
294 #endif /* PERFUSE_DEBUG */
295
296 fd = (int)(long)perfuse_getspecific(pu);
297
298 if (perfuse_inloop(pu))
299 error = xchg_pb_inloop(pu, pb, fd, reply);
300 else
301 error = xchg_pb_early(pu, pb, fd, reply);
302
303 if (error)
304 DERR(EX_SOFTWARE, "xchg_pb failed");
305
306 if (reply == no_reply)
307 return 0;
308
309 foh = perfuse_get_outhdr((perfuse_msg_t *)(void *)pb);
310 #ifdef PERFUSE_DEBUG
311 unique_out = foh->unique;
312
313 if (perfuse_diagflags & PDF_FUSE)
314 DPRINTF("< unique = %"PRId64", nodeid = %"PRId64", "
315 "opcode = %s (%d), "
316 "error = %d\n", unique_out, nodeid,
317 perfuse_opname(opcode), opcode, error);
318
319 if (perfuse_diagflags & PDF_DUMP)
320 perfuse_hexdump((char *)foh, foh->len);
321
322 if (unique_in != unique_out) {
323 printf("%s: packet mismatch unique %"PRId64" vs %"PRId64"\n",
324 __func__, unique_in, unique_out);
325 abort();
326 errx(EX_SOFTWARE, "%s: packet mismatch unique "
327 "%"PRId64" vs %"PRId64"\n",
328 __func__, unique_in, unique_out);
329 }
330 #endif /* PERFUSE_DEBUG */
331
332 if ((expected_len != PERFUSE_UNSPEC_REPLY_LEN) &&
333 (foh->len - sizeof(*foh) < expected_len) &&
334 (foh->error == 0)) {
335 DERRX(EX_PROTOCOL,
336 "Unexpected short reply: received %zd bytes, expected %zd",
337 foh->len - sizeof(*foh), expected_len);
338 }
339
340 if ((expected_len != 0) &&
341 (foh->len - sizeof(*foh) > expected_len))
342 DWARNX("Unexpected long reply");
343
344 /*
345 * Negative Linux errno...
346 */
347 foh->error = -foh->error;
348
349 return foh->error;
350 }
351
352
353 struct fuse_in_header *
354 perfuse_get_inhdr(pm)
355 perfuse_msg_t *pm;
356 {
357 struct puffs_framebuf *pb;
358 struct fuse_in_header *fih;
359 void *hdr;
360 size_t len;
361
362 pb = (struct puffs_framebuf *)(void *)pm;
363 len = sizeof(*fih);
364 if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
365 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
366 if (len != sizeof(*fih))
367 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
368
369 fih = (struct fuse_in_header *)hdr;
370
371 return fih;
372 }
373
374 struct fuse_out_header *
375 perfuse_get_outhdr(pm)
376 perfuse_msg_t *pm;
377 {
378 struct puffs_framebuf *pb;
379 struct fuse_out_header *foh;
380 void *hdr;
381 size_t len;
382
383 pb = (struct puffs_framebuf *)(void *)pm;
384 len = sizeof(*foh);
385 if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
386 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
387 if (len != sizeof(*foh))
388 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
389
390 foh = (struct fuse_out_header *)hdr;
391
392 return foh;
393 }
394
395 char *
396 perfuse_get_inpayload(pm)
397 perfuse_msg_t *pm;
398 {
399 struct puffs_framebuf *pb;
400 struct fuse_in_header *fih;
401 void *hdr;
402 void *payload;
403 size_t len;
404
405 pb = (struct puffs_framebuf *)(void *)pm;
406 len = sizeof(*fih);
407 if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
408 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
409 if (len != sizeof(*fih))
410 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
411
412 fih = (struct fuse_in_header *)hdr;
413
414 len = fih->len - sizeof(*fih);
415 if (puffs_framebuf_getwindow(pb, sizeof(*fih), &payload, &len) != 0)
416 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
417 if (len != fih->len - sizeof(*fih))
418 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
419
420 return (char *)payload;
421 }
422
423 char *
424 perfuse_get_outpayload(pm)
425 perfuse_msg_t *pm;
426 {
427 struct puffs_framebuf *pb;
428 struct fuse_out_header *foh;
429 void *hdr;
430 void *payload;
431 size_t len;
432
433 pb = (struct puffs_framebuf *)(void *)pm;
434 len = sizeof(*foh);
435 if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
436 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
437 if (len != sizeof(*foh))
438 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
439
440 foh = (struct fuse_out_header *)hdr;
441
442 len = foh->len - sizeof(*foh);
443 if (puffs_framebuf_getwindow(pb, sizeof(*foh), &payload, &len) != 0)
444 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
445 if (len != foh->len - sizeof(*foh))
446 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
447
448 return (char *)payload;
449 }
450
451 #define PUFFS_FRAMEBUF_GETWINDOW(pb, offset, data, len) \
452 do { \
453 int pfg_error; \
454 size_t pfg_len = *(len); \
455 \
456 pfg_error = puffs_framebuf_getwindow(pb, offset, data, len); \
457 if (pfg_error != 0) \
458 DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");\
459 \
460 if (*(len) != pfg_len) \
461 DERRX(EX_SOFTWARE, "puffs_framebuf_getwindow size"); \
462 } while (0 /* CONSTCOND */);
463
464 /* ARGSUSED0 */
465 int
466 perfuse_readframe(pu, pufbuf, fd, done)
467 struct puffs_usermount *pu;
468 struct puffs_framebuf *pufbuf;
469 int fd;
470 int *done;
471 {
472 struct fuse_out_header foh;
473 size_t len;
474 ssize_t readen;
475 void *data;
476
477 /*
478 * Read the header
479 */
480 len = sizeof(foh);
481 PUFFS_FRAMEBUF_GETWINDOW(pufbuf, 0, &data, &len);
482
483 switch (readen = recv(fd, data, len, MSG_NOSIGNAL|MSG_PEEK)) {
484 case 0:
485 DWARNX("%s: recv retunred 0", __func__);
486 return ECONNRESET;
487 /* NOTREACHED */
488 break;
489 case -1:
490 if (errno == EAGAIN)
491 return 0;
492 DWARN("%s: recv retunred -1", __func__);
493 return errno;
494 /* NOTREACHED */
495 break;
496 default:
497 break;
498 }
499
500 #ifdef PERFUSE_DEBUG
501 if (readen != len)
502 DERRX(EX_SOFTWARE, "%s: short recv %zd/%zd",
503 __func__, readen, len);
504 #endif
505
506 /*
507 * We have a header, get remaing length to read
508 */
509 if (puffs_framebuf_getdata_atoff(pufbuf, 0, &foh, sizeof(foh)) != 0)
510 DERR(EX_SOFTWARE, "puffs_framebuf_getdata_atoff failed");
511
512 len = foh.len;
513
514 #ifdef PERFUSE_DEBUG
515 if (len > FUSE_BUFSIZE)
516 DERRX(EX_SOFTWARE, "%s: foh.len = %d", __func__, len);
517 #endif
518
519 /*
520 * This is time to reserve space.
521 */
522 if (puffs_framebuf_reserve_space(pufbuf, len) == -1)
523 DERR(EX_OSERR, "puffs_framebuf_reserve_space failed");
524
525 /*
526 * And read the remaining data
527 */
528 PUFFS_FRAMEBUF_GETWINDOW(pufbuf, 0, &data, &len);
529
530 switch (readen = recv(fd, data, len, MSG_NOSIGNAL)) {
531 case 0:
532 DWARNX("%s: recv retunred 0", __func__);
533 return ECONNRESET;
534 /* NOTREACHED */
535 break;
536 case -1:
537 if (errno == EAGAIN)
538 return 0;
539 DWARN("%s: recv retunred -1", __func__);
540 return errno;
541 /* NOTREACHED */
542 break;
543 default:
544 break;
545 }
546
547 #ifdef PERFUSE_DEBUG
548 if (readen != len)
549 DERRX(EX_SOFTWARE, "%s: short recv %zd/%zd",
550 __func__, readen, len);
551 #endif
552
553 *done = 1;
554 return 0;
555 }
556
557 /* ARGSUSED0 */
558 int
559 perfuse_writeframe(pu, pufbuf, fd, done)
560 struct puffs_usermount *pu;
561 struct puffs_framebuf *pufbuf;
562 int fd;
563 int *done;
564 {
565 size_t len;
566 ssize_t written;
567 void *data;
568
569 len = puffs_framebuf_tellsize(pufbuf);
570 PUFFS_FRAMEBUF_GETWINDOW(pufbuf, 0, &data, &len);
571
572 switch (written = send(fd, data, len, MSG_NOSIGNAL)) {
573 case 0:
574 DWARNX("%s: send retunred 0", __func__);
575 return ECONNRESET;
576 /* NOTREACHED */
577 break;
578 case -1:
579 if (errno == EAGAIN)
580 return 0;
581 DWARN("%s: send retunred -1", __func__);
582 return errno;
583 /* NOTREACHED */
584 break;
585 default:
586 break;
587 }
588
589 #ifdef PERFUSE_DEBUG
590 if (written != len)
591 DERRX(EX_SOFTWARE, "%s: short send %zd/%zd",
592 __func__, written, len);
593 #endif
594
595 *done = 1;
596 return 0;
597 }
598
599 /* ARGSUSED0 */
600 int
601 perfuse_cmpframe(pu, pb1, pb2, match)
602 struct puffs_usermount *pu;
603 struct puffs_framebuf *pb1;
604 struct puffs_framebuf *pb2;
605 int *match;
606 {
607 struct fuse_in_header *fih;
608 struct fuse_out_header *foh;
609 uint64_t unique_in;
610 uint64_t unique_out;
611 size_t len;
612
613 len = sizeof(*fih);
614 PUFFS_FRAMEBUF_GETWINDOW(pb1, 0, (void **)&fih, &len);
615 unique_in = fih->unique;
616
617 len = sizeof(*foh);
618 PUFFS_FRAMEBUF_GETWINDOW(pb2, 0, (void **)&foh, &len);
619 unique_out = foh->unique;
620
621 return unique_in != unique_out;
622 }
623
624 /* ARGSUSED0 */
625 void
626 perfuse_gotframe(pu, pb)
627 struct puffs_usermount *pu;
628 struct puffs_framebuf *pb;
629 {
630 struct fuse_out_header *foh;
631 size_t len;
632
633 len = sizeof(*foh);
634 PUFFS_FRAMEBUF_GETWINDOW(pb, 0, (void **)&foh, &len);
635
636 DWARNX("Unexpected frame: unique = %"PRId64", error = %d",
637 foh->unique, foh->error);
638 #ifdef PERFUSE_DEBUG
639 perfuse_hexdump((char *)(void *)foh, foh->len);
640 #endif
641
642 return;
643 }
644
645 void
646 perfuse_fdnotify(pu, fd, what)
647 struct puffs_usermount *pu;
648 int fd;
649 int what;
650 {
651 if (fd != (int)(long)perfuse_getspecific(pu))
652 DERRX(EX_SOFTWARE, "%s: unexpected notification for fd = %d",
653 __func__, fd);
654
655 if ((what != PUFFS_FBIO_READ) && (what != PUFFS_FBIO_WRITE))
656 DERRX(EX_SOFTWARE, "%s: unexpected notification what = 0x%x",
657 __func__, what);
658
659 if (perfuse_unmount(pu) != 0)
660 DWARN("unmount() failed");
661
662 if (shutdown(fd, SHUT_RDWR) != 0)
663 DWARN("shutdown() failed");
664
665 if (perfuse_diagflags & PDF_MISC)
666 DPRINTF("Exit");
667
668 exit(0);
669
670 /* NOTREACHED */
671 return;
672 }
673