xref: /dragonfly/sys/vfs/fuse/fuse_device.c (revision 0b2c5ee3)
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 	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
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
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
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 
108 	while ((fip = TAILQ_FIRST(&fmp->reply_head))) {
109 		TAILQ_REMOVE(&fmp->reply_head, fip, reply_entry);
110 		if (fuse_ipc_test_and_set_replied(fip))
111 			wakeup(fip);
112 	}
113 }
114 
115 static int
116 fuse_device_read(struct dev_read_args *ap)
117 {
118 	struct uio *uio = ap->a_uio;
119 	struct fuse_mount *fmp;
120 	struct fuse_ipc *fip;
121 	int error;
122 
123 	error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
124 	if (error)
125 		return error;
126 
127 	if (fuse_test_dead(fmp))
128 		return ENOTCONN;
129 
130 	mtx_lock(&fmp->ipc_lock);
131 	while (!(fip = TAILQ_FIRST(&fmp->request_head))) {
132 		error = mtxsleep(fmp, &fmp->ipc_lock, PCATCH, "ftxc", 0);
133 		if (fuse_test_dead(fmp)) {
134 			fuse_device_clear(fmp);
135 			mtx_unlock(&fmp->ipc_lock);
136 			fuse_dbg("error=%d dead\n", error);
137 			return ENOTCONN;
138 		}
139 		if (error) {
140 			mtx_unlock(&fmp->ipc_lock);
141 			fuse_dbg("error=%d\n", error);
142 			return error;
143 		}
144 	}
145 	TAILQ_REMOVE(&fmp->request_head, fip, request_entry);
146 	mtx_unlock(&fmp->ipc_lock);
147 
148 	fuse_dbgipc(fip, 0, "");
149 
150 	if (uio->uio_resid < fuse_in_size(fip))
151 		return EILSEQ;
152 
153 	return uiomove(fuse_in(fip), fuse_in_size(fip), uio);
154 }
155 
156 static int
157 fuse_device_write(struct dev_write_args *ap)
158 {
159 	struct uio *uio = ap->a_uio;
160 	struct fuse_mount *fmp;
161 	struct fuse_ipc *fip;
162 	struct fuse_buf fb;
163 	struct fuse_in_header *ihd;
164 	struct fuse_out_header *ohd;
165 	int error;
166 
167 	error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
168 	if (error)
169 		return error;
170 
171 	if (uio->uio_resid < sizeof(*ohd))
172 		return EILSEQ;
173 
174 	fuse_buf_alloc(&fb, uio->uio_resid);
175 	error = uiomove(fb.buf, uio->uio_resid, uio);
176 	if (error) {
177 		fuse_buf_free(&fb);
178 		return error;
179 	}
180 	ohd = fb.buf;
181 
182 	mtx_lock(&fmp->ipc_lock);
183 	TAILQ_FOREACH(fip, &fmp->reply_head, reply_entry) {
184 		if (fip->unique == ohd->unique) {
185 			TAILQ_REMOVE(&fmp->reply_head, fip, reply_entry);
186 			break;
187 		}
188 	}
189 	mtx_unlock(&fmp->ipc_lock);
190 
191 	if (!fip) {
192 		fuse_dbg("unique=%ju not found\n", ohd->unique);
193 		fuse_buf_free(&fb);
194 		return ENOMSG;
195 	}
196 
197 	fip->reply = fb;
198 	ihd = fuse_in(fip);
199 
200 	/* Non zero ohd->error is not /dev/fuse write error. */
201 	if (ohd->error == -ENOSYS) {
202 		fuse_set_nosys(fmp, ihd->opcode);
203 		fuse_dbgipc(fip, ohd->error, "ENOSYS");
204 	} else if (!ohd->error && fuse_audit_length(ihd, ohd)) {
205 		error = EPROTO;
206 		fuse_dbgipc(fip, error, "audit");
207 	} else
208 		fuse_dbgipc(fip, 0, "");
209 
210 	/* Complete the IPC regardless of above result. */
211 	if (fuse_ipc_test_and_set_replied(fip))
212 		wakeup(fip);
213 
214 	return error;
215 }
216 
217 static void filt_fusedevdetach(struct knote*);
218 static int filt_fusedevread(struct knote*, long);
219 static int filt_fusedevwrite(struct knote*, long);
220 
221 static struct filterops fusedevread_filterops =
222 	{ FILTEROP_ISFD,
223 	  NULL, filt_fusedevdetach, filt_fusedevread };
224 static struct filterops fusedevwrite_filterops =
225 	{ FILTEROP_ISFD,
226 	  NULL, filt_fusedevdetach, filt_fusedevwrite };
227 
228 static int
229 fuse_device_kqfilter(struct dev_kqfilter_args *ap)
230 {
231 	struct knote *kn = ap->a_kn;
232 	struct klist *klist;
233 	struct fuse_mount *fmp;
234 	int error;
235 
236 	error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
237 	if (error) {
238 		ap->a_result = error;
239 		return 0;
240 	}
241 
242 	ap->a_result = 0;
243 
244 	switch (kn->kn_filter) {
245 	case EVFILT_READ:
246 		kn->kn_fop = &fusedevread_filterops;
247 		kn->kn_hook = (caddr_t)fmp;
248 		break;
249 	case EVFILT_WRITE:
250 		kn->kn_fop = &fusedevwrite_filterops;
251 		kn->kn_hook = (caddr_t)fmp;
252 		break;
253 	default:
254 		ap->a_result = EOPNOTSUPP;
255 		return 0;
256 	}
257 
258 	klist = &fmp->kq.ki_note;
259 	knote_insert(klist, kn);
260 
261 	return 0;
262 }
263 
264 static void
265 filt_fusedevdetach(struct knote *kn)
266 {
267 	struct fuse_mount *fmp = (struct fuse_mount*)kn->kn_hook;
268 	struct klist *klist = &fmp->kq.ki_note;
269 
270 	knote_remove(klist, kn);
271 }
272 
273 static int
274 filt_fusedevread(struct knote *kn, long hint)
275 {
276 	struct fuse_mount *fmp = (struct fuse_mount*)kn->kn_hook;
277 	int ready = 0;
278 
279 	mtx_lock(&fmp->ipc_lock);
280 	if (!TAILQ_EMPTY(&fmp->request_head))
281 		ready = 1;
282 	mtx_unlock(&fmp->ipc_lock);
283 
284 	return ready;
285 }
286 
287 static int
288 filt_fusedevwrite(struct knote *kn, long hint)
289 {
290 	return 1;
291 }
292 
293 static struct dev_ops fuse_device_cdevsw = {
294 	{ "fuse", 0, D_MPSAFE, },
295 	.d_open = fuse_device_open,
296 	.d_close = fuse_device_close,
297 	.d_read = fuse_device_read,
298 	.d_write = fuse_device_write,
299 	.d_kqfilter = fuse_device_kqfilter,
300 };
301 
302 int
303 fuse_device_init(void)
304 {
305 	fuse_dev = make_dev(&fuse_device_cdevsw, 0, UID_ROOT, GID_OPERATOR,
306 	    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, "fuse");
307 
308 	if (!fuse_dev)
309 		return ENOMEM;
310 
311 	return 0;
312 }
313 
314 void
315 fuse_device_cleanup(void)
316 {
317 	KKASSERT(fuse_dev);
318 	destroy_dev(fuse_dev);
319 }
320