xref: /dragonfly/sys/vfs/fuse/fuse_device.c (revision 5e83d98b)
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
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
48 fuse_device_open(struct dev_open_args *ap)
49 {
50 	struct fuse_mount *fmp;
51 
52 	fmp = kmalloc(sizeof(*fmp), M_TEMP, M_WAITOK | M_ZERO);
53 	KKASSERT(fmp);
54 
55 	refcount_init(&fmp->refcnt, 1);
56 	devfs_set_cdevpriv(ap->a_fp, fmp, fuse_cdevpriv_dtor);
57 	fuse_dbg("open %s\n", ap->a_head.a_dev->si_name);
58 
59 	return 0;
60 }
61 
62 static int
63 fuse_device_close(struct dev_close_args *ap)
64 {
65 	struct fuse_mount *fmp;
66 	int error;
67 
68 	error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
69 	if (error)
70 		return error;
71 	KKASSERT(fmp);
72 
73 	/* XXX Can't call this on device close due to devfs bug... */
74 	//fuse_cdevpriv_close(fmp);
75 	fuse_dbg("close %s\n", ap->a_head.a_dev->si_name);
76 
77 	return 0;
78 }
79 
80 static int
81 fuse_cdevpriv_close(struct fuse_mount *fmp)
82 {
83 	if (!fmp->devvp) {
84 		fuse_print("/dev/%s not associated with FUSE mount\n",
85 		    fuse_dev->si_name);
86 		return ENODEV;
87 	}
88 
89 	mtx_lock(&fmp->mnt_lock);
90 	if (fuse_mount_kill(fmp) == -1)
91 		KNOTE(&fmp->kq.ki_note, 0);
92 	KKASSERT(fmp->devvp);
93 	mtx_unlock(&fmp->mnt_lock);
94 
95 	return 0;
96 }
97 
98 /* Call with ->ipc_lock held. */
99 static void
100 fuse_device_clear(struct fuse_mount *fmp)
101 {
102 	struct fuse_ipc *fip;
103 
104 	while ((fip = TAILQ_FIRST(&fmp->request_head)))
105 		TAILQ_REMOVE(&fmp->request_head, fip, request_entry);
106 
107 	while ((fip = TAILQ_FIRST(&fmp->reply_head))) {
108 		TAILQ_REMOVE(&fmp->reply_head, fip, reply_entry);
109 		if (fuse_ipc_test_and_set_replied(fip))
110 			wakeup(fip);
111 	}
112 }
113 
114 static int
115 fuse_device_read(struct dev_read_args *ap)
116 {
117 	struct uio *uio = ap->a_uio;
118 	struct fuse_mount *fmp;
119 	struct fuse_ipc *fip;
120 	int error;
121 
122 	error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
123 	if (error)
124 		return error;
125 
126 	if (fuse_test_dead(fmp))
127 		return ENOTCONN;
128 
129 	mtx_lock(&fmp->ipc_lock);
130 	while (!(fip = TAILQ_FIRST(&fmp->request_head))) {
131 		error = mtxsleep(fmp, &fmp->ipc_lock, PCATCH, "ftxc", 0);
132 		if (fuse_test_dead(fmp)) {
133 			fuse_device_clear(fmp);
134 			mtx_unlock(&fmp->ipc_lock);
135 			fuse_dbg("error=%d dead\n", error);
136 			return ENOTCONN;
137 		}
138 		if (error) {
139 			mtx_unlock(&fmp->ipc_lock);
140 			fuse_dbg("error=%d\n", error);
141 			return error;
142 		}
143 	}
144 	TAILQ_REMOVE(&fmp->request_head, fip, request_entry);
145 	mtx_unlock(&fmp->ipc_lock);
146 
147 	fuse_dbgipc(fip, 0, "");
148 
149 	if (uio->uio_resid < fuse_in_size(fip))
150 		return EILSEQ;
151 
152 	return uiomove(fuse_in(fip), fuse_in_size(fip), uio);
153 }
154 
155 static int
156 fuse_device_write(struct dev_write_args *ap)
157 {
158 	struct uio *uio = ap->a_uio;
159 	struct fuse_mount *fmp;
160 	struct fuse_ipc *fip;
161 	struct fuse_buf fb;
162 	struct fuse_in_header *ihd;
163 	struct fuse_out_header *ohd;
164 	int error;
165 
166 	error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
167 	if (error)
168 		return error;
169 
170 	if (uio->uio_resid < sizeof(*ohd))
171 		return EILSEQ;
172 
173 	fuse_buf_alloc(&fb, uio->uio_resid);
174 	error = uiomove(fb.buf, uio->uio_resid, uio);
175 	if (error) {
176 		fuse_buf_free(&fb);
177 		return error;
178 	}
179 	ohd = fb.buf;
180 
181 	mtx_lock(&fmp->ipc_lock);
182 	TAILQ_FOREACH(fip, &fmp->reply_head, reply_entry) {
183 		if (fip->unique == ohd->unique) {
184 			TAILQ_REMOVE(&fmp->reply_head, fip, reply_entry);
185 			break;
186 		}
187 	}
188 	mtx_unlock(&fmp->ipc_lock);
189 
190 	if (!fip) {
191 		fuse_dbg("unique=%ju not found\n", ohd->unique);
192 		fuse_buf_free(&fb);
193 		return ENOMSG;
194 	}
195 
196 	fip->reply = fb;
197 	ihd = fuse_in(fip);
198 
199 	/* Non zero ohd->error is not /dev/fuse write error. */
200 	if (ohd->error == -ENOSYS) {
201 		fuse_set_nosys(fmp, ihd->opcode);
202 		fuse_dbgipc(fip, ohd->error, "ENOSYS");
203 	} else if (!ohd->error && fuse_audit_length(ihd, ohd)) {
204 		error = EPROTO;
205 		fuse_dbgipc(fip, error, "audit");
206 	} else
207 		fuse_dbgipc(fip, 0, "");
208 
209 	/* Complete the IPC regardless of above result. */
210 	if (fuse_ipc_test_and_set_replied(fip))
211 		wakeup(fip);
212 
213 	return error;
214 }
215 
216 static void filt_fusedevdetach(struct knote*);
217 static int filt_fusedevread(struct knote*, long);
218 static int filt_fusedevwrite(struct knote*, long);
219 
220 static struct filterops fusedevread_filterops =
221 	{ FILTEROP_ISFD,
222 	  NULL, filt_fusedevdetach, filt_fusedevread };
223 static struct filterops fusedevwrite_filterops =
224 	{ FILTEROP_ISFD,
225 	  NULL, filt_fusedevdetach, filt_fusedevwrite };
226 
227 static int
228 fuse_device_kqfilter(struct dev_kqfilter_args *ap)
229 {
230 	struct knote *kn = ap->a_kn;
231 	struct klist *klist;
232 	struct fuse_mount *fmp;
233 	int error;
234 
235 	error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
236 	if (error) {
237 		ap->a_result = error;
238 		return 0;
239 	}
240 
241 	ap->a_result = 0;
242 
243 	switch (kn->kn_filter) {
244 	case EVFILT_READ:
245 		kn->kn_fop = &fusedevread_filterops;
246 		kn->kn_hook = (caddr_t)fmp;
247 		break;
248 	case EVFILT_WRITE:
249 		kn->kn_fop = &fusedevwrite_filterops;
250 		kn->kn_hook = (caddr_t)fmp;
251 		break;
252 	default:
253 		ap->a_result = EOPNOTSUPP;
254 		return 0;
255 	}
256 
257 	klist = &fmp->kq.ki_note;
258 	knote_insert(klist, kn);
259 
260 	return 0;
261 }
262 
263 static void
264 filt_fusedevdetach(struct knote *kn)
265 {
266 	struct fuse_mount *fmp = (struct fuse_mount*)kn->kn_hook;
267 	struct klist *klist = &fmp->kq.ki_note;
268 
269 	knote_remove(klist, kn);
270 }
271 
272 static int
273 filt_fusedevread(struct knote *kn, long hint)
274 {
275 	struct fuse_mount *fmp = (struct fuse_mount*)kn->kn_hook;
276 	int ready = 0;
277 
278 	mtx_lock(&fmp->ipc_lock);
279 	if (!TAILQ_EMPTY(&fmp->request_head))
280 		ready = 1;
281 	mtx_unlock(&fmp->ipc_lock);
282 
283 	return ready;
284 }
285 
286 static int
287 filt_fusedevwrite(struct knote *kn, long hint)
288 {
289 	return 1;
290 }
291 
292 static struct dev_ops fuse_device_cdevsw = {
293 	{ "fuse", 0, D_MPSAFE, },
294 	.d_open = fuse_device_open,
295 	.d_close = fuse_device_close,
296 	.d_read = fuse_device_read,
297 	.d_write = fuse_device_write,
298 	.d_kqfilter = fuse_device_kqfilter,
299 };
300 
301 int
302 fuse_device_init(void)
303 {
304 	fuse_dev = make_dev(&fuse_device_cdevsw, 0, UID_ROOT, GID_OPERATOR,
305 	    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, "fuse");
306 
307 	if (!fuse_dev)
308 		return ENOMEM;
309 
310 	return 0;
311 }
312 
313 void
314 fuse_device_cleanup(void)
315 {
316 	KKASSERT(fuse_dev);
317 	destroy_dev(fuse_dev);
318 }
319