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