Home | History | Annotate | Line # | Download | only in perfused
msg.c revision 1.7
      1 /*  $NetBSD: msg.c,v 1.7 2010/09/20 06:45:38 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, PERFUSE_SOCKTYPE, 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 any FUSE packet
     72 	 * will fit.
     73 	 */
     74 	opt = FUSE_BUFSIZE;
     75 	if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) != 0)
     76 		DWARN("%s: setsockopt SO_SNDBUF to %d failed", __func__, opt);
     77 
     78 	opt = FUSE_BUFSIZE;
     79 	if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) != 0)
     80 		DWARN("%s: setsockopt SO_RCVBUF to %d failed", __func__, opt);
     81 
     82 	/*
     83 	 * Request peer credentials
     84 	 */
     85 	opt = 1;
     86 	if (setsockopt(s, 0, LOCAL_CREDS, &opt, sizeof(opt)) != 0)
     87 		DWARN("%s: setsockopt LOCAL_CREDS failed", __func__);
     88 
     89 	if (bind(s, sa, (socklen_t )sun.sun_len) == -1)
     90 		err(EX_OSERR, "cannot open \"%s\" socket", _PATH_FUSE);
     91 
     92 #if (PERFUSE_SOCKTYPE == SOCK_DGRAM)
     93 	if (connect(s, sa, (socklen_t )sun.sun_len) == -1)
     94 		err(EX_OSERR, "cannot open \"%s\" socket", _PATH_FUSE);
     95 #else
     96 	if (listen(s, 1) == -1)
     97 		err(EX_OSERR, "listen failed");
     98 #endif
     99 
    100 	return s;
    101 }
    102 
    103 
    104 void *
    105 perfuse_recv_early(fd, sockcred, sockcred_len)
    106 	int fd;
    107 	struct sockcred *sockcred;
    108 	size_t sockcred_len;
    109 {
    110 	struct fuse_out_header foh;
    111 	size_t len;
    112 	char *buf;
    113 	struct msghdr msg;
    114 	char cmsg_buf[sizeof(struct cmsghdr) + SOCKCREDSIZE(NGROUPS_MAX)];
    115 	struct cmsghdr *cmsg = (struct cmsghdr *)(void *)&cmsg_buf;
    116 	struct sockcred *sc = (struct sockcred *)(void *)(cmsg + 1);
    117 	struct iovec iov;
    118 
    119 	len = sizeof(foh);
    120 
    121 	/*
    122 	 * We use the complicated recvmsg because we want peer creds.
    123 	 */
    124 	iov.iov_base = &foh;
    125 	iov.iov_len = len;
    126 	msg.msg_name = NULL;
    127 	msg.msg_namelen = 0;
    128 	msg.msg_iov = &iov;
    129 	msg.msg_iovlen = 1;
    130 	msg.msg_control = cmsg;
    131 	msg.msg_controllen = sizeof(cmsg_buf);
    132 	msg.msg_flags = 0;
    133 
    134 	if (recvmsg(fd, &msg, MSG_NOSIGNAL|MSG_PEEK) != (ssize_t)len) {
    135 		DWARN("short recv (header)");
    136 		return NULL;
    137 	}
    138 
    139 	if (cmsg->cmsg_type != SCM_CREDS) {
    140 		DWARNX("No SCM_CREDS");
    141 		return NULL;
    142 	}
    143 
    144 	if (sockcred != NULL)
    145 		(void)memcpy(sockcred, sc,
    146 			     MIN(cmsg->cmsg_len - sizeof(*cmsg), sockcred_len));
    147 
    148 
    149 	len = foh.len;
    150 	if ((buf = malloc(len)) == NULL)
    151 		err(EX_OSERR, "malloc(%zd) failed", len);
    152 
    153 	if (recv(fd, buf, len, MSG_NOSIGNAL) != (ssize_t)len) {
    154 		DWARN("short recv (frame)");
    155 		return NULL;
    156 	}
    157 
    158 	return buf;
    159 }
    160 
    161 
    162 perfuse_msg_t *
    163 perfuse_new_pb (pu, opc, opcode, payload_len, cred)
    164 	struct puffs_usermount *pu;
    165 	puffs_cookie_t opc;
    166 	int opcode;
    167 	size_t payload_len;
    168 	const struct puffs_cred *cred;
    169 {
    170 	struct puffs_framebuf *pb;
    171 	struct fuse_in_header *fih;
    172 	struct puffs_cc *pcc;
    173 	uint64_t nodeid;
    174 	void *data;
    175 	size_t len;
    176 
    177 	if ((pb = puffs_framebuf_make()) == NULL)
    178 		DERR(EX_OSERR, "puffs_framebuf_make failed");
    179 
    180 	len = payload_len + sizeof(*fih);
    181 	nodeid = (opc != 0) ? perfuse_get_ino(pu, opc) : PERFUSE_UNKNOWN_INO;
    182 
    183 	if (puffs_framebuf_reserve_space(pb, len) != 0)
    184 		DERR(EX_OSERR, "puffs_framebuf_reserve_space failed");
    185 
    186 	if (puffs_framebuf_getwindow(pb, 0, &data, &len) != 0)
    187 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
    188 	if (len != payload_len + sizeof(*fih))
    189 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short len");
    190 
    191 	(void)memset(data, 0, len);
    192 	fih = (struct fuse_in_header *)data;
    193 	fih->len = (uint32_t)len;
    194 	fih->opcode = opcode;
    195 	fih->unique = perfuse_next_unique(pu);
    196 	fih->nodeid = nodeid;
    197 	fih->uid = (uid_t)-1;
    198 	fih->gid = (gid_t)-1;
    199 	fih->pid = 0;
    200 	if (cred != NULL) {
    201 		(void)puffs_cred_getuid(cred, &fih->uid);
    202 		(void)puffs_cred_getgid(cred, &fih->gid);
    203 	}
    204 	if ((pcc = puffs_cc_getcc(pu)) != NULL)
    205 		(void)puffs_cc_getcaller(pcc, (pid_t *)&fih->pid, NULL);
    206 
    207 	return (perfuse_msg_t *)(void *)pb;
    208 }
    209 
    210 /*
    211  * framebuf send/receive primitives based on pcc are
    212  * not available until puffs mainloop is entered.
    213  * This xchg_pb_inloop() variant allow early communication.
    214  */
    215 static int
    216 xchg_pb_early(pu, pb, fd, reply)
    217 	struct puffs_usermount *pu;
    218 	struct puffs_framebuf *pb;
    219 	int fd;
    220 	enum perfuse_xchg_pb_reply reply;
    221 {
    222 	int done;
    223 	int error;
    224 
    225 	done = 0;
    226 	while (done == 0) {
    227 		if ((error = perfuse_writeframe(pu, pb, fd, &done)) != 0)
    228 			return error;
    229 	}
    230 
    231 	if (reply == no_reply) {
    232 		puffs_framebuf_destroy(pb);
    233 		return 0;
    234 	} else {
    235 		puffs_framebuf_recycle(pb);
    236 	}
    237 
    238 	done = 0;
    239 	while (done == 0) {
    240 		if ((error = perfuse_readframe(pu, pb, fd, &done)) != 0)
    241 			return error;
    242 	}
    243 
    244 	return 0;
    245 }
    246 
    247 static int
    248 xchg_pb_inloop(pu, pb, fd, reply)
    249 	struct puffs_usermount *pu;
    250 	struct puffs_framebuf *pb;
    251 	int fd;
    252 	enum perfuse_xchg_pb_reply reply;
    253 {
    254 	struct puffs_cc *pcc;
    255 	int error;
    256 
    257 	if (reply == no_reply) {
    258 		error = puffs_framev_enqueue_justsend(pu, fd, pb, 0, 0);
    259 	} else {
    260 		pcc = puffs_cc_getcc(pu);
    261 		error = puffs_framev_enqueue_cc(pcc, fd, pb, 0);
    262 	}
    263 
    264 	return error;
    265 }
    266 
    267 int
    268 perfuse_xchg_pb(pu, pm, expected_len, reply)
    269 	struct puffs_usermount *pu;
    270 	perfuse_msg_t *pm;
    271 	size_t expected_len;
    272 	enum perfuse_xchg_pb_reply reply;
    273 {
    274 	struct puffs_framebuf *pb = (struct puffs_framebuf *)(void *)pm;
    275 	int fd;
    276 	int error;
    277 	struct fuse_out_header *foh;
    278 #ifdef PERFUSE_DEBUG
    279 	struct fuse_in_header *fih;
    280 	uint64_t nodeid;
    281 	int opcode;
    282 	uint64_t unique_in;
    283 	uint64_t unique_out;
    284 
    285 	fih = perfuse_get_inhdr(pm);
    286 	unique_in = fih->unique;
    287 	nodeid = fih->nodeid;
    288 	opcode = fih->opcode;
    289 
    290 	if (perfuse_diagflags & PDF_FUSE)
    291 		DPRINTF("> unique = %"PRId64", nodeid = %"PRId64", "
    292 			"opcode = %s (%d)\n",
    293 			unique_in, nodeid, perfuse_opname(opcode), opcode);
    294 
    295 	if (perfuse_diagflags & PDF_DUMP)
    296 		perfuse_hexdump((char *)fih, fih->len);
    297 
    298 #endif /* PERFUSE_DEBUG */
    299 
    300 	fd = (int)(long)perfuse_getspecific(pu);
    301 
    302 	if (perfuse_inloop(pu))
    303 		error = xchg_pb_inloop(pu, pb, fd, reply);
    304 	else
    305 		error = xchg_pb_early(pu, pb, fd, reply);
    306 
    307 	if (error)
    308 		DERR(EX_SOFTWARE, "xchg_pb failed");
    309 
    310 	if (reply == no_reply)
    311 		return 0;
    312 
    313 	foh = perfuse_get_outhdr((perfuse_msg_t *)(void *)pb);
    314 #ifdef PERFUSE_DEBUG
    315 	unique_out = foh->unique;
    316 
    317 	if (perfuse_diagflags & PDF_FUSE)
    318 		DPRINTF("< unique = %"PRId64", nodeid = %"PRId64", "
    319 			"opcode = %s (%d), "
    320 			"error = %d\n", unique_out, nodeid,
    321 			perfuse_opname(opcode), opcode, error);
    322 
    323 	if (perfuse_diagflags & PDF_DUMP)
    324 		perfuse_hexdump((char *)foh, foh->len);
    325 
    326 	if (unique_in != unique_out) {
    327 		printf("%s: packet mismatch unique %"PRId64" vs %"PRId64"\n",
    328 		     __func__, unique_in, unique_out);
    329 		abort();
    330 		errx(EX_SOFTWARE, "%s: packet mismatch unique "
    331 		     "%"PRId64" vs %"PRId64"\n",
    332 		     __func__, unique_in, unique_out);
    333 	}
    334 #endif /* PERFUSE_DEBUG */
    335 
    336 	if ((expected_len != PERFUSE_UNSPEC_REPLY_LEN) &&
    337 	    (foh->len - sizeof(*foh) < expected_len) &&
    338 	    (foh->error == 0)) {
    339 		DERRX(EX_PROTOCOL,
    340 		     "Unexpected short reply: received %zd bytes, expected %zd",
    341 		     foh->len - sizeof(*foh), expected_len);
    342 	}
    343 
    344 	if ((expected_len != 0) &&
    345 	    (foh->len - sizeof(*foh) > expected_len))
    346 		DWARNX("Unexpected long reply");
    347 
    348 	/*
    349 	 * Negative Linux errno...
    350 	 */
    351 	foh->error = -foh->error;
    352 
    353 	return foh->error;
    354 }
    355 
    356 
    357 struct fuse_in_header *
    358 perfuse_get_inhdr(pm)
    359 	perfuse_msg_t *pm;
    360 {
    361 	struct puffs_framebuf *pb;
    362 	struct fuse_in_header *fih;
    363 	void *hdr;
    364 	size_t len;
    365 
    366 	pb = (struct puffs_framebuf *)(void *)pm;
    367 	len = sizeof(*fih);
    368 	if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
    369 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
    370 	if (len != sizeof(*fih))
    371 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
    372 
    373 	fih = (struct fuse_in_header *)hdr;
    374 
    375 	return fih;
    376 }
    377 
    378 struct fuse_out_header *
    379 perfuse_get_outhdr(pm)
    380 	perfuse_msg_t *pm;
    381 {
    382 	struct puffs_framebuf *pb;
    383 	struct fuse_out_header *foh;
    384 	void *hdr;
    385 	size_t len;
    386 
    387 	pb = (struct puffs_framebuf *)(void *)pm;
    388 	len = sizeof(*foh);
    389 	if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
    390 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
    391 	if (len != sizeof(*foh))
    392 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
    393 
    394 	foh = (struct fuse_out_header *)hdr;
    395 
    396 	return foh;
    397 }
    398 
    399 char *
    400 perfuse_get_inpayload(pm)
    401 	perfuse_msg_t *pm;
    402 {
    403 	struct puffs_framebuf *pb;
    404 	struct fuse_in_header *fih;
    405 	void *hdr;
    406 	void *payload;
    407 	size_t len;
    408 
    409 	pb = (struct puffs_framebuf *)(void *)pm;
    410 	len = sizeof(*fih);
    411 	if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
    412 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
    413 	if (len != sizeof(*fih))
    414 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
    415 
    416 	fih = (struct fuse_in_header *)hdr;
    417 
    418 	len = fih->len - sizeof(*fih);
    419 	if (puffs_framebuf_getwindow(pb, sizeof(*fih), &payload, &len) != 0)
    420 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
    421 	if (len != fih->len - sizeof(*fih))
    422 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
    423 
    424 	return (char *)payload;
    425 }
    426 
    427 char *
    428 perfuse_get_outpayload(pm)
    429 	perfuse_msg_t *pm;
    430 {
    431 	struct puffs_framebuf *pb;
    432 	struct fuse_out_header *foh;
    433 	void *hdr;
    434 	void *payload;
    435 	size_t len;
    436 
    437 	pb = (struct puffs_framebuf *)(void *)pm;
    438 	len = sizeof(*foh);
    439 	if (puffs_framebuf_getwindow(pb, 0, &hdr, &len) != 0)
    440 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
    441 	if (len != sizeof(*foh))
    442 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
    443 
    444 	foh = (struct fuse_out_header *)hdr;
    445 
    446 	len = foh->len - sizeof(*foh);
    447 	if (puffs_framebuf_getwindow(pb, sizeof(*foh), &payload, &len) != 0)
    448 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");
    449 	if (len != foh->len - sizeof(*foh))
    450 		DERR(EX_SOFTWARE, "puffs_framebuf_getwindow short header");
    451 
    452 	return (char *)payload;
    453 }
    454 
    455 #define PUFFS_FRAMEBUF_GETWINDOW(pb, offset, data, len) 		     \
    456 	do {								     \
    457 		int pfg_error;						     \
    458 		size_t pfg_len = *(len);				     \
    459 									     \
    460 		pfg_error = puffs_framebuf_getwindow(pb, offset, data, len); \
    461 		if (pfg_error != 0)					     \
    462 			DERR(EX_SOFTWARE, "puffs_framebuf_getwindow failed");\
    463 									     \
    464 		if (*(len) != pfg_len)					     \
    465 			DERRX(EX_SOFTWARE, "puffs_framebuf_getwindow size"); \
    466 	} while (0 /* CONSTCOND */);
    467 
    468 /* ARGSUSED0 */
    469 int
    470 perfuse_readframe(pu, pufbuf, fd, done)
    471 	struct puffs_usermount *pu;
    472 	struct puffs_framebuf *pufbuf;
    473 	int fd;
    474 	int *done;
    475 {
    476 	struct fuse_out_header foh;
    477 	size_t offset;
    478 	size_t remain;
    479 	ssize_t readen;
    480 	void *data;
    481 	int peek = 0;
    482 
    483 #if (PERFUSE_SOCKTYPE == SOCK_DGRAM)
    484 	peek = MSG_PEEK;
    485 #endif
    486 	offset = puffs_framebuf_telloff(pufbuf);
    487 
    488 	/*
    489 	 * Read the header
    490 	 * MSG_PEEK is used so that this code works for SOCK_DGRAM
    491 	 * socket. The loop is only needed to work with SOCK_STREAM.
    492 	 */
    493 	while (offset < sizeof(foh)) {
    494 		remain = sizeof(foh) - offset;
    495 		PUFFS_FRAMEBUF_GETWINDOW(pufbuf, offset, &data, &remain);
    496 
    497 		switch (readen = recv(fd, data, remain, MSG_NOSIGNAL|peek)) {
    498 		case 0:
    499 			DWARNX("%s: recv retunred 0", __func__);
    500 			return ECONNRESET;
    501 			/* NOTREACHED */
    502 			break;
    503 		case -1:
    504 			if (errno == EAGAIN)
    505 				return 0;
    506 			DWARN("%s: recv retunred -1", __func__);
    507 			return errno;
    508 			/* NOTREACHED */
    509 			break;
    510 		default:
    511 #if defined(PERFUSE_DEBUG) && (PERFUSE_SOCKTYPE == SOCK_DGRAM)
    512 			if (readen != remain)
    513 				DERRX(EX_SOFTWARE, "%s: short recv %zd/%zd",
    514 				      __func__, readen, remain);
    515 #endif
    516 			break;
    517 		}
    518 
    519 		offset += readen;
    520 		if (puffs_framebuf_seekset(pufbuf, offset) == -1)
    521 			DERR(EX_OSERR, "puffs_framebuf_seekset failed");
    522 	}
    523 
    524 #if (PERFUSE_SOCKTYPE == SOCK_DGRAM)
    525 	/*
    526 	 * We had a peek at the header, now really read it.
    527 	 */
    528 	offset = 0;
    529 #endif
    530 
    531 	/*
    532 	 * We have a header, get remaing length to read
    533 	 */
    534 	if (puffs_framebuf_getdata_atoff(pufbuf, 0, &foh, sizeof(foh)) != 0)
    535 		DERR(EX_SOFTWARE, "puffs_framebuf_getdata_atoff failed");
    536 ;
    537 #ifdef PERFUSE_DEBUG
    538 		if (foh.len > FUSE_BUFSIZE)
    539 			DERRX(EX_SOFTWARE, "%s: foh.len = %d (this is huge!)",
    540 			      __func__, foh.len);
    541 #endif
    542 
    543 	/*
    544 	 * If we have only readen the header so far,
    545 	 * this is time to reserve space.
    546 	 */
    547 	remain = foh.len - offset;
    548 	if (offset == sizeof(foh))
    549 		if (puffs_framebuf_reserve_space(pufbuf, remain) == -1)
    550 			DERR(EX_OSERR, "puffs_framebuf_reserve_space failed");
    551 
    552 
    553 	/*
    554 	 * And read the remaining data
    555 	 */
    556 	while (remain != 0) {
    557 		PUFFS_FRAMEBUF_GETWINDOW(pufbuf, offset, &data, &remain);
    558 
    559 		switch (readen = recv(fd, data, remain, MSG_NOSIGNAL)) {
    560 		case 0:
    561 			DWARNX("%s: recv retunred 0", __func__);
    562 			return ECONNRESET;
    563 			/* NOTREACHED */
    564 			break;
    565 		case -1:
    566 			if (errno == EAGAIN)
    567 				return 0;
    568 			DWARN("%s: recv retunred -1", __func__);
    569 			return errno;
    570 			/* NOTREACHED */
    571 			break;
    572 		default:
    573 #if defined(PERFUSE_DEBUG) && (PERFUSE_SOCKTYPE == SOCK_DGRAM)
    574 			if (readen != remain)
    575 				DERRX(EX_SOFTWARE, "%s: short recv %zd/%zd",
    576 				      __func__, readen, remain);
    577 #endif
    578 			break;
    579 		}
    580 
    581 		offset += readen;
    582 		remain -= readen;
    583 
    584 		if (puffs_framebuf_seekset(pufbuf, offset) == -1)
    585 			DERR(EX_OSERR, "puffs_framebuf_seekset failed");
    586 	}
    587 
    588 	*done = 1;
    589 	return 0;
    590 }
    591 
    592 /* ARGSUSED0 */
    593 int
    594 perfuse_writeframe(pu, pufbuf, fd, done)
    595 	struct puffs_usermount *pu;
    596 	struct puffs_framebuf *pufbuf;
    597 	int fd;
    598 	int *done;
    599 {
    600 	size_t offset;
    601 	size_t len;
    602 	ssize_t written;
    603 	size_t remain;
    604 	void *data;
    605 
    606 	offset = puffs_framebuf_telloff(pufbuf);
    607 	len = puffs_framebuf_tellsize(pufbuf) - offset;
    608 	remain = len;
    609 
    610 	while (remain != 0) {
    611 		PUFFS_FRAMEBUF_GETWINDOW(pufbuf, offset, &data, &len);
    612 
    613 		switch (written = send(fd, data, remain, MSG_NOSIGNAL)) {
    614 		case 0:
    615 			DWARNX("%s: send retunred 0", __func__);
    616 			return ECONNRESET;
    617 			/* NOTREACHED */
    618 			break;
    619 		case -1:
    620 			if (errno == EAGAIN)
    621 				return 0;
    622 			DWARN("%s: send retunred -1", __func__);
    623 			return errno;
    624 			/* NOTREACHED */
    625 			break;
    626 		default:
    627 #if defined(PERFUSE_DEBUG) && (PERFUSE_SOCKTYPE == SOCK_DGRAM)
    628 			if (written != remain)
    629 				DERRX(EX_SOFTWARE, "%s: short send %zd/%zd",
    630 				      __func__, written, remain);
    631 #endif
    632 			break;
    633 		}
    634 
    635 		remain -= written;
    636 		offset += written;
    637 
    638 		if (puffs_framebuf_seekset(pufbuf, offset) == -1)
    639 			DERR(EX_OSERR, "puffs_framebuf_seekset failed");
    640 	}
    641 
    642 	*done = 1;
    643 	return 0;
    644 }
    645 
    646 /* ARGSUSED0 */
    647 int
    648 perfuse_cmpframe(pu, pb1, pb2, match)
    649 	struct puffs_usermount *pu;
    650 	struct puffs_framebuf *pb1;
    651 	struct puffs_framebuf *pb2;
    652 	int *match;
    653 {
    654 	struct fuse_in_header *fih;
    655 	struct fuse_out_header *foh;
    656 	uint64_t unique_in;
    657 	uint64_t unique_out;
    658 	size_t len;
    659 
    660 	len = sizeof(*fih);
    661 	PUFFS_FRAMEBUF_GETWINDOW(pb1, 0, (void **)&fih, &len);
    662 	unique_in = fih->unique;
    663 
    664 	len = sizeof(*foh);
    665 	PUFFS_FRAMEBUF_GETWINDOW(pb2, 0, (void **)&foh, &len);
    666 	unique_out = foh->unique;
    667 
    668 	return unique_in != unique_out;
    669 }
    670 
    671 /* ARGSUSED0 */
    672 void
    673 perfuse_gotframe(pu, pb)
    674 	struct puffs_usermount *pu;
    675 	struct puffs_framebuf *pb;
    676 {
    677 	struct fuse_out_header *foh;
    678 	size_t len;
    679 
    680 	len = sizeof(*foh);
    681 	PUFFS_FRAMEBUF_GETWINDOW(pb, 0, (void **)&foh, &len);
    682 
    683 	DWARNX("Unexpected frame: unique = %"PRId64", error = %d",
    684 	       foh->unique, foh->error);
    685 #ifdef PERFUSE_DEBUG
    686 	perfuse_hexdump((char *)(void *)foh, foh->len);
    687 #endif
    688 
    689 	return;
    690 }
    691 
    692 void
    693 perfuse_fdnotify(pu, fd, what)
    694 	struct puffs_usermount *pu;
    695 	int fd;
    696 	int what;
    697 {
    698 	if (fd != (int)(long)perfuse_getspecific(pu))
    699 		DERRX(EX_SOFTWARE, "%s: unexpected notification for fd = %d",
    700 		      __func__, fd);
    701 
    702 	if ((what != PUFFS_FBIO_READ) && (what != PUFFS_FBIO_WRITE))
    703 		DERRX(EX_SOFTWARE, "%s: unexpected notification what = 0x%x",
    704 		      __func__, what);
    705 
    706 	if (perfuse_unmount(pu) != 0)
    707 		DWARN("unmount() failed");
    708 
    709 	if (shutdown(fd, SHUT_RDWR) != 0)
    710 		DWARN("shutdown() failed");
    711 
    712 	if (perfuse_diagflags & PDF_MISC)
    713 		DPRINTF("Exit");
    714 
    715 	exit(0);
    716 
    717 	/* NOTREACHED */
    718 	return;
    719 }
    720