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