xref: /netbsd-src/usr.sbin/perfused/msg.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
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