xref: /freebsd/lib/libcuse/cuse_lib.c (revision d0b2dbfa0ecf2bbc9709efc5e20baf8e4b44bbbf)
1 /*-
2  * Copyright (c) 2010-2022 Hans Petter Selasky. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <stdio.h>
27 #include <stdint.h>
28 #include <pthread.h>
29 #include <signal.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 
36 #include <sys/types.h>
37 #include <sys/queue.h>
38 #include <sys/fcntl.h>
39 #include <sys/mman.h>
40 #include <sys/param.h>
41 
42 #include <fs/cuse/cuse_ioctl.h>
43 
44 #include "cuse.h"
45 
46 int	cuse_debug_level;
47 
48 #ifdef HAVE_DEBUG
49 static const char *cuse_cmd_str(int cmd);
50 
51 #define	DPRINTF(...) do {			\
52 	if (cuse_debug_level != 0)		\
53 		printf(__VA_ARGS__);		\
54 } while (0)
55 #else
56 #define	DPRINTF(...) do { } while (0)
57 #endif
58 
59 struct cuse_vm_allocation {
60 	uint8_t *ptr;
61 	uint32_t size;
62 };
63 
64 struct cuse_dev_entered {
65 	TAILQ_ENTRY(cuse_dev_entered) entry;
66 	pthread_t thread;
67 	void   *per_file_handle;
68 	struct cuse_dev *cdev;
69 	int	cmd;
70 	int	is_local;
71 	int	got_signal;
72 };
73 
74 struct cuse_dev {
75 	TAILQ_ENTRY(cuse_dev) entry;
76 	const struct cuse_methods *mtod;
77 	void   *priv0;
78 	void   *priv1;
79 };
80 
81 static int f_cuse = -1;
82 
83 static pthread_mutex_t m_cuse;
84 static TAILQ_HEAD(, cuse_dev) h_cuse __guarded_by(m_cuse);
85 static TAILQ_HEAD(, cuse_dev_entered) h_cuse_entered __guarded_by(m_cuse);
86 static struct cuse_vm_allocation a_cuse[CUSE_ALLOC_UNIT_MAX]
87     __guarded_by(m_cuse);
88 
89 #define	CUSE_LOCK() \
90 	pthread_mutex_lock(&m_cuse)
91 
92 #define	CUSE_UNLOCK() \
93 	pthread_mutex_unlock(&m_cuse)
94 
95 int
96 cuse_init(void)
97 {
98 	pthread_mutexattr_t attr;
99 
100 	f_cuse = open("/dev/cuse", O_RDWR);
101 	if (f_cuse < 0) {
102 		if (feature_present("cuse") == 0)
103 			return (CUSE_ERR_NOT_LOADED);
104 		else
105 			return (CUSE_ERR_INVALID);
106 	}
107 	pthread_mutexattr_init(&attr);
108 	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
109 	pthread_mutex_init(&m_cuse, &attr);
110 
111 	TAILQ_INIT(&h_cuse);
112 	TAILQ_INIT(&h_cuse_entered);
113 
114 	return (0);
115 }
116 
117 int
118 cuse_uninit(void)
119 {
120 	int f;
121 
122 	if (f_cuse < 0)
123 		return (CUSE_ERR_INVALID);
124 
125 	f = f_cuse;
126 	f_cuse = -1;
127 
128 	close(f);
129 
130 	pthread_mutex_destroy(&m_cuse);
131 
132 	memset(a_cuse, 0, sizeof(a_cuse));
133 
134 	return (0);
135 }
136 
137 unsigned long
138 cuse_vmoffset(void *_ptr)
139 {
140 	uint8_t *ptr_min;
141 	uint8_t *ptr_max;
142 	uint8_t *ptr = _ptr;
143 	unsigned long remainder;
144 	unsigned long n;
145 
146 	CUSE_LOCK();
147 	for (n = remainder = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
148 		if (a_cuse[n].ptr == NULL)
149 			continue;
150 
151 		ptr_min = a_cuse[n].ptr;
152 		ptr_max = a_cuse[n].ptr + a_cuse[n].size - 1;
153 
154 		if ((ptr >= ptr_min) && (ptr <= ptr_max)) {
155 			remainder = (ptr - ptr_min);
156 			break;
157 		}
158 	}
159 	CUSE_UNLOCK();
160 
161 	return ((n << CUSE_ALLOC_UNIT_SHIFT) + remainder);
162 }
163 
164 void   *
165 cuse_vmalloc(unsigned size)
166 {
167 	struct cuse_alloc_info info;
168 	unsigned long pgsize;
169 	unsigned long x;
170 	unsigned long m;
171 	unsigned long n;
172 	void *ptr;
173 	int error;
174 
175 	/* some sanity checks */
176 	if (f_cuse < 0 || size < 1 || size > CUSE_ALLOC_BYTES_MAX)
177 		return (NULL);
178 
179 	memset(&info, 0, sizeof(info));
180 
181 	pgsize = getpagesize();
182 	info.page_count = howmany(size, pgsize);
183 
184 	/* compute how many units the allocation needs */
185 	m = howmany(size, 1 << CUSE_ALLOC_UNIT_SHIFT);
186 	if (m == 0 || m > CUSE_ALLOC_UNIT_MAX)
187 		return (NULL);
188 
189 	CUSE_LOCK();
190 	for (n = 0; n <= CUSE_ALLOC_UNIT_MAX - m; ) {
191 		if (a_cuse[n].size != 0) {
192 			/* skip to next available unit, depending on allocation size */
193 			n += howmany(a_cuse[n].size, 1 << CUSE_ALLOC_UNIT_SHIFT);
194 			continue;
195 		}
196 		/* check if there are "m" free units ahead */
197 		for (x = 1; x != m; x++) {
198 			if (a_cuse[n + x].size != 0)
199 				break;
200 		}
201 		if (x != m) {
202 			/* skip to next available unit, if any */
203 			n += x + 1;
204 			continue;
205 		}
206 		/* reserve this unit by setting the size to a non-zero value */
207 		a_cuse[n].size = size;
208 		CUSE_UNLOCK();
209 
210 		info.alloc_nr = n;
211 
212 		error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_MEMORY, &info);
213 
214 		if (error == 0) {
215 			ptr = mmap(NULL, info.page_count * pgsize,
216 			    PROT_READ | PROT_WRITE,
217 			    MAP_SHARED, f_cuse, n << CUSE_ALLOC_UNIT_SHIFT);
218 
219 			if (ptr != MAP_FAILED) {
220 				CUSE_LOCK();
221 				a_cuse[n].ptr = ptr;
222 				CUSE_UNLOCK();
223 
224 				return (ptr);		/* success */
225 			}
226 
227 			(void) ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
228 		}
229 
230 		CUSE_LOCK();
231 		a_cuse[n].size = 0;
232 		n++;
233 	}
234 	CUSE_UNLOCK();
235 	return (NULL);			/* failure */
236 }
237 
238 int
239 cuse_is_vmalloc_addr(void *ptr)
240 {
241 	int n;
242 
243 	if (f_cuse < 0 || ptr == NULL)
244 		return (0);		/* false */
245 
246 	CUSE_LOCK();
247 	for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
248 		if (a_cuse[n].ptr == ptr)
249 			break;
250 	}
251 	CUSE_UNLOCK();
252 
253 	return (n != CUSE_ALLOC_UNIT_MAX);
254 }
255 
256 void
257 cuse_vmfree(void *ptr)
258 {
259 	struct cuse_vm_allocation temp;
260 	struct cuse_alloc_info info;
261 	int error;
262 	int n;
263 
264 	if (f_cuse < 0 || ptr == NULL)
265 		return;
266 
267 	CUSE_LOCK();
268 	for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
269 		if (a_cuse[n].ptr != ptr)
270 			continue;
271 
272 		temp = a_cuse[n];
273 
274 		CUSE_UNLOCK();
275 
276 		munmap(temp.ptr, temp.size);
277 
278 		memset(&info, 0, sizeof(info));
279 
280 		info.alloc_nr = n;
281 
282 		error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
283 
284 		if (error != 0) {
285 			/* ignore any errors */
286 			DPRINTF("Freeing memory failed: %d\n", errno);
287 		}
288 		CUSE_LOCK();
289 
290 		a_cuse[n].ptr = NULL;
291 		a_cuse[n].size = 0;
292 
293 		break;
294 	}
295 	CUSE_UNLOCK();
296 }
297 
298 int
299 cuse_alloc_unit_number_by_id(int *pnum, int id)
300 {
301 	int error;
302 
303 	if (f_cuse < 0)
304 		return (CUSE_ERR_INVALID);
305 
306 	*pnum = (id & CUSE_ID_MASK);
307 
308 	error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT_BY_ID, pnum);
309 	if (error)
310 		return (CUSE_ERR_NO_MEMORY);
311 
312 	return (0);
313 
314 }
315 
316 int
317 cuse_free_unit_number_by_id(int num, int id)
318 {
319 	int error;
320 
321 	if (f_cuse < 0)
322 		return (CUSE_ERR_INVALID);
323 
324 	if (num != -1 || id != -1)
325 		num = (id & CUSE_ID_MASK) | (num & 0xFF);
326 
327 	error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT_BY_ID, &num);
328 	if (error)
329 		return (CUSE_ERR_NO_MEMORY);
330 
331 	return (0);
332 }
333 
334 int
335 cuse_alloc_unit_number(int *pnum)
336 {
337 	int error;
338 
339 	if (f_cuse < 0)
340 		return (CUSE_ERR_INVALID);
341 
342 	error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT, pnum);
343 	if (error)
344 		return (CUSE_ERR_NO_MEMORY);
345 
346 	return (0);
347 }
348 
349 int
350 cuse_free_unit_number(int num)
351 {
352 	int error;
353 
354 	if (f_cuse < 0)
355 		return (CUSE_ERR_INVALID);
356 
357 	error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT, &num);
358 	if (error)
359 		return (CUSE_ERR_NO_MEMORY);
360 
361 	return (0);
362 }
363 
364 struct cuse_dev *
365 cuse_dev_create(const struct cuse_methods *mtod, void *priv0, void *priv1,
366     uid_t _uid, gid_t _gid, int _perms, const char *_fmt,...)
367 {
368 	struct cuse_create_dev info;
369 	struct cuse_dev *cdev;
370 	va_list args;
371 	int error;
372 
373 	if (f_cuse < 0)
374 		return (NULL);
375 
376 	cdev = malloc(sizeof(*cdev));
377 	if (cdev == NULL)
378 		return (NULL);
379 
380 	memset(cdev, 0, sizeof(*cdev));
381 
382 	cdev->mtod = mtod;
383 	cdev->priv0 = priv0;
384 	cdev->priv1 = priv1;
385 
386 	memset(&info, 0, sizeof(info));
387 
388 	info.dev = cdev;
389 	info.user_id = _uid;
390 	info.group_id = _gid;
391 	info.permissions = _perms;
392 
393 	va_start(args, _fmt);
394 	vsnprintf(info.devname, sizeof(info.devname), _fmt, args);
395 	va_end(args);
396 
397 	error = ioctl(f_cuse, CUSE_IOCTL_CREATE_DEV, &info);
398 	if (error) {
399 		free(cdev);
400 		return (NULL);
401 	}
402 	CUSE_LOCK();
403 	TAILQ_INSERT_TAIL(&h_cuse, cdev, entry);
404 	CUSE_UNLOCK();
405 
406 	return (cdev);
407 }
408 
409 
410 void
411 cuse_dev_destroy(struct cuse_dev *cdev)
412 {
413 	int error;
414 
415 	if (f_cuse < 0)
416 		return;
417 
418 	CUSE_LOCK();
419 	TAILQ_REMOVE(&h_cuse, cdev, entry);
420 	CUSE_UNLOCK();
421 
422 	error = ioctl(f_cuse, CUSE_IOCTL_DESTROY_DEV, &cdev);
423 	if (error)
424 		return;
425 
426 	free(cdev);
427 }
428 
429 void   *
430 cuse_dev_get_priv0(struct cuse_dev *cdev)
431 {
432 	return (cdev->priv0);
433 }
434 
435 void   *
436 cuse_dev_get_priv1(struct cuse_dev *cdev)
437 {
438 	return (cdev->priv1);
439 }
440 
441 void
442 cuse_dev_set_priv0(struct cuse_dev *cdev, void *priv)
443 {
444 	cdev->priv0 = priv;
445 }
446 
447 void
448 cuse_dev_set_priv1(struct cuse_dev *cdev, void *priv)
449 {
450 	cdev->priv1 = priv;
451 }
452 
453 int
454 cuse_wait_and_process(void)
455 {
456 	pthread_t curr = pthread_self();
457 	struct cuse_dev_entered *pe;
458 	struct cuse_dev_entered enter;
459 	struct cuse_command info;
460 	struct cuse_dev *cdev;
461 	int error;
462 
463 	if (f_cuse < 0)
464 		return (CUSE_ERR_INVALID);
465 
466 	error = ioctl(f_cuse, CUSE_IOCTL_GET_COMMAND, &info);
467 	if (error)
468 		return (CUSE_ERR_OTHER);
469 
470 	cdev = info.dev;
471 
472 	CUSE_LOCK();
473 	enter.thread = curr;
474 	enter.per_file_handle = (void *)info.per_file_handle;
475 	enter.cmd = info.command;
476 	enter.is_local = 0;
477 	enter.got_signal = 0;
478 	enter.cdev = cdev;
479 	TAILQ_INSERT_TAIL(&h_cuse_entered, &enter, entry);
480 	CUSE_UNLOCK();
481 
482 	DPRINTF("cuse: Command = %d = %s, flags = %d, arg = 0x%08x, ptr = 0x%08x\n",
483 	    (int)info.command, cuse_cmd_str(info.command), (int)info.fflags,
484 	    (int)info.argument, (int)info.data_pointer);
485 
486 	switch (info.command) {
487 	case CUSE_CMD_OPEN:
488 		if (cdev->mtod->cm_open != NULL)
489 			error = (cdev->mtod->cm_open) (cdev, (int)info.fflags);
490 		else
491 			error = 0;
492 		break;
493 
494 	case CUSE_CMD_CLOSE:
495 
496 		/* wait for other threads to stop */
497 
498 		while (1) {
499 
500 			error = 0;
501 
502 			CUSE_LOCK();
503 			TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
504 				if (pe->cdev != cdev)
505 					continue;
506 				if (pe->thread == curr)
507 					continue;
508 				if (pe->per_file_handle !=
509 				    enter.per_file_handle)
510 					continue;
511 				pe->got_signal = 1;
512 				pthread_kill(pe->thread, SIGHUP);
513 				error = CUSE_ERR_BUSY;
514 			}
515 			CUSE_UNLOCK();
516 
517 			if (error == 0)
518 				break;
519 			else
520 				usleep(10000);
521 		}
522 
523 		if (cdev->mtod->cm_close != NULL)
524 			error = (cdev->mtod->cm_close) (cdev, (int)info.fflags);
525 		else
526 			error = 0;
527 		break;
528 
529 	case CUSE_CMD_READ:
530 		if (cdev->mtod->cm_read != NULL) {
531 			error = (cdev->mtod->cm_read) (cdev, (int)info.fflags,
532 			    (void *)info.data_pointer, (int)info.argument);
533 		} else {
534 			error = CUSE_ERR_INVALID;
535 		}
536 		break;
537 
538 	case CUSE_CMD_WRITE:
539 		if (cdev->mtod->cm_write != NULL) {
540 			error = (cdev->mtod->cm_write) (cdev, (int)info.fflags,
541 			    (void *)info.data_pointer, (int)info.argument);
542 		} else {
543 			error = CUSE_ERR_INVALID;
544 		}
545 		break;
546 
547 	case CUSE_CMD_IOCTL:
548 		if (cdev->mtod->cm_ioctl != NULL) {
549 			error = (cdev->mtod->cm_ioctl) (cdev, (int)info.fflags,
550 			    (unsigned int)info.argument, (void *)info.data_pointer);
551 		} else {
552 			error = CUSE_ERR_INVALID;
553 		}
554 		break;
555 
556 	case CUSE_CMD_POLL:
557 		if (cdev->mtod->cm_poll != NULL) {
558 			error = (cdev->mtod->cm_poll) (cdev, (int)info.fflags,
559 			    (int)info.argument);
560 		} else {
561 			error = CUSE_POLL_ERROR;
562 		}
563 		break;
564 
565 	case CUSE_CMD_SIGNAL:
566 		CUSE_LOCK();
567 		TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
568 			if (pe->cdev != cdev)
569 				continue;
570 			if (pe->thread == curr)
571 				continue;
572 			if (pe->per_file_handle !=
573 			    enter.per_file_handle)
574 				continue;
575 			pe->got_signal = 1;
576 			pthread_kill(pe->thread, SIGHUP);
577 		}
578 		CUSE_UNLOCK();
579 		break;
580 
581 	default:
582 		error = CUSE_ERR_INVALID;
583 		break;
584 	}
585 
586 	DPRINTF("cuse: Command error = %d for %s\n",
587 	    error, cuse_cmd_str(info.command));
588 
589 	CUSE_LOCK();
590 	TAILQ_REMOVE(&h_cuse_entered, &enter, entry);
591 	CUSE_UNLOCK();
592 
593 	/* we ignore any sync command failures */
594 	ioctl(f_cuse, CUSE_IOCTL_SYNC_COMMAND, &error);
595 
596 	return (0);
597 }
598 
599 static struct cuse_dev_entered *
600 cuse_dev_get_entered(void)
601 {
602 	struct cuse_dev_entered *pe;
603 	pthread_t curr = pthread_self();
604 
605 	CUSE_LOCK();
606 	TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
607 		if (pe->thread == curr)
608 			break;
609 	}
610 	CUSE_UNLOCK();
611 	return (pe);
612 }
613 
614 void
615 cuse_dev_set_per_file_handle(struct cuse_dev *cdev, void *handle)
616 {
617 	struct cuse_dev_entered *pe;
618 
619 	pe = cuse_dev_get_entered();
620 	if (pe == NULL || pe->cdev != cdev)
621 		return;
622 
623 	pe->per_file_handle = handle;
624 	ioctl(f_cuse, CUSE_IOCTL_SET_PFH, &handle);
625 }
626 
627 void   *
628 cuse_dev_get_per_file_handle(struct cuse_dev *cdev)
629 {
630 	struct cuse_dev_entered *pe;
631 
632 	pe = cuse_dev_get_entered();
633 	if (pe == NULL || pe->cdev != cdev)
634 		return (NULL);
635 
636 	return (pe->per_file_handle);
637 }
638 
639 void
640 cuse_set_local(int val)
641 {
642 	struct cuse_dev_entered *pe;
643 
644 	pe = cuse_dev_get_entered();
645 	if (pe == NULL)
646 		return;
647 
648 	pe->is_local = val;
649 }
650 
651 #ifdef HAVE_DEBUG
652 static const char *
653 cuse_cmd_str(int cmd)
654 {
655 	static const char *str[CUSE_CMD_MAX] = {
656 		[CUSE_CMD_NONE] = "none",
657 		[CUSE_CMD_OPEN] = "open",
658 		[CUSE_CMD_CLOSE] = "close",
659 		[CUSE_CMD_READ] = "read",
660 		[CUSE_CMD_WRITE] = "write",
661 		[CUSE_CMD_IOCTL] = "ioctl",
662 		[CUSE_CMD_POLL] = "poll",
663 		[CUSE_CMD_SIGNAL] = "signal",
664 		[CUSE_CMD_SYNC] = "sync",
665 	};
666 
667 	if ((cmd >= 0) && (cmd < CUSE_CMD_MAX) &&
668 	    (str[cmd] != NULL))
669 		return (str[cmd]);
670 	else
671 		return ("unknown");
672 }
673 
674 #endif
675 
676 int
677 cuse_get_local(void)
678 {
679 	struct cuse_dev_entered *pe;
680 
681 	pe = cuse_dev_get_entered();
682 	if (pe == NULL)
683 		return (0);
684 
685 	return (pe->is_local);
686 }
687 
688 int
689 cuse_copy_out(const void *src, void *user_dst, int len)
690 {
691 	struct cuse_data_chunk info;
692 	struct cuse_dev_entered *pe;
693 	int error;
694 
695 	if ((f_cuse < 0) || (len < 0))
696 		return (CUSE_ERR_INVALID);
697 
698 	pe = cuse_dev_get_entered();
699 	if (pe == NULL)
700 		return (CUSE_ERR_INVALID);
701 
702 	DPRINTF("cuse: copy_out(%p,%p,%d), cmd = %d = %s\n",
703 	    src, user_dst, len, pe->cmd, cuse_cmd_str(pe->cmd));
704 
705 	if (pe->is_local) {
706 		memcpy(user_dst, src, len);
707 	} else {
708 		info.local_ptr = (uintptr_t)src;
709 		info.peer_ptr = (uintptr_t)user_dst;
710 		info.length = len;
711 
712 		error = ioctl(f_cuse, CUSE_IOCTL_WRITE_DATA, &info);
713 		if (error) {
714 			DPRINTF("cuse: copy_out() error = %d\n", errno);
715 			return (CUSE_ERR_FAULT);
716 		}
717 	}
718 	return (0);
719 }
720 
721 int
722 cuse_copy_in(const void *user_src, void *dst, int len)
723 {
724 	struct cuse_data_chunk info;
725 	struct cuse_dev_entered *pe;
726 	int error;
727 
728 	if ((f_cuse < 0) || (len < 0))
729 		return (CUSE_ERR_INVALID);
730 
731 	pe = cuse_dev_get_entered();
732 	if (pe == NULL)
733 		return (CUSE_ERR_INVALID);
734 
735 	DPRINTF("cuse: copy_in(%p,%p,%d), cmd = %d = %s\n",
736 	    user_src, dst, len, pe->cmd, cuse_cmd_str(pe->cmd));
737 
738 	if (pe->is_local) {
739 		memcpy(dst, user_src, len);
740 	} else {
741 		info.local_ptr = (uintptr_t)dst;
742 		info.peer_ptr = (uintptr_t)user_src;
743 		info.length = len;
744 
745 		error = ioctl(f_cuse, CUSE_IOCTL_READ_DATA, &info);
746 		if (error) {
747 			DPRINTF("cuse: copy_in() error = %d\n", errno);
748 			return (CUSE_ERR_FAULT);
749 		}
750 	}
751 	return (0);
752 }
753 
754 struct cuse_dev *
755 cuse_dev_get_current(int *pcmd)
756 {
757 	struct cuse_dev_entered *pe;
758 
759 	pe = cuse_dev_get_entered();
760 	if (pe == NULL) {
761 		if (pcmd != NULL)
762 			*pcmd = 0;
763 		return (NULL);
764 	}
765 	if (pcmd != NULL)
766 		*pcmd = pe->cmd;
767 	return (pe->cdev);
768 }
769 
770 int
771 cuse_got_peer_signal(void)
772 {
773 	struct cuse_dev_entered *pe;
774 
775 	pe = cuse_dev_get_entered();
776 	if (pe == NULL)
777 		return (CUSE_ERR_INVALID);
778 
779 	if (pe->got_signal)
780 		return (0);
781 
782 	return (CUSE_ERR_OTHER);
783 }
784 
785 void
786 cuse_poll_wakeup(void)
787 {
788 	int error = 0;
789 
790 	if (f_cuse < 0)
791 		return;
792 
793 	ioctl(f_cuse, CUSE_IOCTL_SELWAKEUP, &error);
794 }
795