xref: /dragonfly/sys/vfs/fuse/fuse_device.c (revision 1a8e5e4c)
1 /*-
2  * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org>
3  * Copyright (c) 2019 The DragonFly Project
4  * 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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include "fuse.h"
29 
30 #include <sys/conf.h>
31 #include <sys/device.h>
32 #include <sys/devfs.h>
33 #include <sys/uio.h>
34 
35 static int fuse_cdevpriv_close(struct fuse_mount*);
36 static struct cdev *fuse_dev;
37 
38 static void
fuse_cdevpriv_dtor(void * data)39 fuse_cdevpriv_dtor(void *data)
40 {
41 	struct fuse_mount *fmp = data;
42 
43 	if (!fuse_cdevpriv_close(fmp))
44 		fuse_mount_free(fmp);
45 }
46 
47 static int
fuse_device_open(struct dev_open_args * ap)48 fuse_device_open(struct dev_open_args *ap)
49 {
50 	struct fuse_mount *fmp;
51 	struct file *fp = ap->a_fpp ? *ap->a_fpp : NULL;
52 
53 	fmp = kmalloc(sizeof(*fmp), M_TEMP, M_WAITOK | M_ZERO);
54 	KKASSERT(fmp);
55 
56 	refcount_init(&fmp->refcnt, 1);
57 	devfs_set_cdevpriv(fp, fmp, fuse_cdevpriv_dtor);
58 	fuse_dbg("open %s\n", ap->a_head.a_dev->si_name);
59 
60 	return 0;
61 }
62 
63 static int
fuse_device_close(struct dev_close_args * ap)64 fuse_device_close(struct dev_close_args *ap)
65 {
66 	struct fuse_mount *fmp;
67 	int error;
68 
69 	error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
70 	if (error)
71 		return error;
72 	KKASSERT(fmp);
73 
74 	/* XXX Can't call this on device close due to devfs bug... */
75 	//fuse_cdevpriv_close(fmp);
76 	fuse_dbg("close %s\n", ap->a_head.a_dev->si_name);
77 
78 	return 0;
79 }
80 
81 static int
fuse_cdevpriv_close(struct fuse_mount * fmp)82 fuse_cdevpriv_close(struct fuse_mount *fmp)
83 {
84 	if (!fmp->devvp) {
85 		fuse_print("/dev/%s not associated with FUSE mount\n",
86 		    fuse_dev->si_name);
87 		return ENODEV;
88 	}
89 
90 	mtx_lock(&fmp->mnt_lock);
91 	if (fuse_mount_kill(fmp) == -1)
92 		KNOTE(&fmp->kq.ki_note, 0);
93 	KKASSERT(fmp->devvp);
94 	mtx_unlock(&fmp->mnt_lock);
95 
96 	return 0;
97 }
98 
99 /* Call with ->ipc_lock held. */
100 static void
fuse_device_clear(struct fuse_mount * fmp)101 fuse_device_clear(struct fuse_mount *fmp)
102 {
103 	struct fuse_ipc *fip;
104 
105 	while ((fip = TAILQ_FIRST(&fmp->request_head))) {
106 		TAILQ_REMOVE(&fmp->request_head, fip, request_entry);
107 		if (atomic_swap_int(&fip->sent, 1) == -1)
108 			wakeup(fip);
109 	}
110 
111 	while ((fip = TAILQ_FIRST(&fmp->reply_head))) {
112 		TAILQ_REMOVE(&fmp->reply_head, fip, reply_entry);
113 		if (fuse_ipc_test_and_set_replied(fip))
114 			wakeup(fip);
115 	}
116 }
117 
118 static int
fuse_device_read(struct dev_read_args * ap)119 fuse_device_read(struct dev_read_args *ap)
120 {
121 	struct uio *uio = ap->a_uio;
122 	struct fuse_mount *fmp;
123 	struct fuse_ipc *fip;
124 	int error;
125 
126 	error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
127 	if (error)
128 		return error;
129 
130 	if (fuse_test_dead(fmp))
131 		return ENOTCONN;
132 
133 	mtx_lock(&fmp->ipc_lock);
134 	while (!(fip = TAILQ_FIRST(&fmp->request_head))) {
135 		error = mtxsleep(fmp, &fmp->ipc_lock, PCATCH, "ftxc", 0);
136 		if (fuse_test_dead(fmp)) {
137 			fuse_device_clear(fmp);
138 			mtx_unlock(&fmp->ipc_lock);
139 			fuse_dbg("error=%d dead\n", error);
140 			return ENOTCONN;
141 		}
142 		if (error) {
143 			mtx_unlock(&fmp->ipc_lock);
144 			fuse_dbg("error=%d\n", error);
145 			return error;
146 		}
147 	}
148 	TAILQ_REMOVE(&fmp->request_head, fip, request_entry);
149 	mtx_unlock(&fmp->ipc_lock);
150 
151 	fuse_dbgipc(fip, 0, "");
152 
153 	if (uio->uio_resid < fuse_in_size(fip))
154 		error = EILSEQ;
155 	else
156 		error = uiomove(fuse_in(fip), fuse_in_size(fip), uio);
157 
158 	if (atomic_swap_int(&fip->sent, 1) == -1)
159 		wakeup(fip);
160 
161 	return error;
162 }
163 
164 static int
fuse_device_write(struct dev_write_args * ap)165 fuse_device_write(struct dev_write_args *ap)
166 {
167 	struct uio *uio = ap->a_uio;
168 	struct fuse_mount *fmp;
169 	struct fuse_ipc *fip;
170 	struct fuse_buf fb;
171 	struct fuse_in_header *ihd;
172 	struct fuse_out_header *ohd;
173 	int error;
174 
175 	error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
176 	if (error)
177 		return error;
178 
179 	if (uio->uio_resid < sizeof(*ohd))
180 		return EILSEQ;
181 
182 	fuse_buf_alloc(&fb, uio->uio_resid);
183 	error = uiomove(fb.buf, uio->uio_resid, uio);
184 	if (error) {
185 		fuse_buf_free(&fb);
186 		return error;
187 	}
188 	ohd = fb.buf;
189 
190 	mtx_lock(&fmp->ipc_lock);
191 	TAILQ_FOREACH(fip, &fmp->reply_head, reply_entry) {
192 		if (fip->unique == ohd->unique) {
193 			TAILQ_REMOVE(&fmp->reply_head, fip, reply_entry);
194 			break;
195 		}
196 	}
197 	mtx_unlock(&fmp->ipc_lock);
198 
199 	if (!fip) {
200 		fuse_dbg("unique=%ju not found\n", ohd->unique);
201 		fuse_buf_free(&fb);
202 		return ENOMSG;
203 	}
204 
205 	fip->reply = fb;
206 	ihd = fuse_in(fip);
207 
208 	/* Non zero ohd->error is not /dev/fuse write error. */
209 	if (ohd->error == -ENOSYS) {
210 		fuse_set_nosys(fmp, ihd->opcode);
211 		fuse_dbgipc(fip, ohd->error, "ENOSYS");
212 	} else if (!ohd->error && fuse_audit_length(ihd, ohd)) {
213 		error = EPROTO;
214 		fuse_dbgipc(fip, error, "audit");
215 	} else
216 		fuse_dbgipc(fip, 0, "");
217 
218 	/* Complete the IPC regardless of above result. */
219 	if (fuse_ipc_test_and_set_replied(fip))
220 		wakeup(fip);
221 
222 	return error;
223 }
224 
225 static void filt_fusedevdetach(struct knote*);
226 static int filt_fusedevread(struct knote*, long);
227 static int filt_fusedevwrite(struct knote*, long);
228 
229 static struct filterops fusedevread_filterops =
230 	{ FILTEROP_ISFD,
231 	  NULL, filt_fusedevdetach, filt_fusedevread };
232 static struct filterops fusedevwrite_filterops =
233 	{ FILTEROP_ISFD,
234 	  NULL, filt_fusedevdetach, filt_fusedevwrite };
235 
236 static int
fuse_device_kqfilter(struct dev_kqfilter_args * ap)237 fuse_device_kqfilter(struct dev_kqfilter_args *ap)
238 {
239 	struct knote *kn = ap->a_kn;
240 	struct klist *klist;
241 	struct fuse_mount *fmp;
242 	int error;
243 
244 	error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
245 	if (error) {
246 		ap->a_result = error;
247 		return 0;
248 	}
249 
250 	ap->a_result = 0;
251 
252 	switch (kn->kn_filter) {
253 	case EVFILT_READ:
254 		kn->kn_fop = &fusedevread_filterops;
255 		kn->kn_hook = (caddr_t)fmp;
256 		break;
257 	case EVFILT_WRITE:
258 		kn->kn_fop = &fusedevwrite_filterops;
259 		kn->kn_hook = (caddr_t)fmp;
260 		break;
261 	default:
262 		ap->a_result = EOPNOTSUPP;
263 		return 0;
264 	}
265 
266 	klist = &fmp->kq.ki_note;
267 	knote_insert(klist, kn);
268 
269 	return 0;
270 }
271 
272 static void
filt_fusedevdetach(struct knote * kn)273 filt_fusedevdetach(struct knote *kn)
274 {
275 	struct fuse_mount *fmp = (struct fuse_mount*)kn->kn_hook;
276 	struct klist *klist = &fmp->kq.ki_note;
277 
278 	knote_remove(klist, kn);
279 }
280 
281 static int
filt_fusedevread(struct knote * kn,long hint)282 filt_fusedevread(struct knote *kn, long hint)
283 {
284 	struct fuse_mount *fmp = (struct fuse_mount*)kn->kn_hook;
285 	int ready = 0;
286 
287 	mtx_lock(&fmp->ipc_lock);
288 	if (!TAILQ_EMPTY(&fmp->request_head))
289 		ready = 1;
290 	mtx_unlock(&fmp->ipc_lock);
291 
292 	return ready;
293 }
294 
295 static int
filt_fusedevwrite(struct knote * kn,long hint)296 filt_fusedevwrite(struct knote *kn, long hint)
297 {
298 	return 1;
299 }
300 
301 static struct dev_ops fuse_device_cdevsw = {
302 	{ "fuse", 0, D_MPSAFE, },
303 	.d_open = fuse_device_open,
304 	.d_close = fuse_device_close,
305 	.d_read = fuse_device_read,
306 	.d_write = fuse_device_write,
307 	.d_kqfilter = fuse_device_kqfilter,
308 };
309 
310 int
fuse_device_init(void)311 fuse_device_init(void)
312 {
313 	fuse_dev = make_dev(&fuse_device_cdevsw, 0, UID_ROOT, GID_OPERATOR,
314 	    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, "fuse");
315 
316 	if (!fuse_dev)
317 		return ENOMEM;
318 
319 	return 0;
320 }
321 
322 void
fuse_device_cleanup(void)323 fuse_device_cleanup(void)
324 {
325 	KKASSERT(fuse_dev);
326 	destroy_dev(fuse_dev);
327 }
328