2 * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org>
3 * Copyright (c) 2019 The DragonFly Project
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
31 #include <sys/device.h>
32 #include <sys/devfs.h>
35 static int fuse_cdevpriv_close(struct fuse_mount*);
36 static struct cdev *fuse_dev;
39 fuse_cdevpriv_dtor(void *data)
41 struct fuse_mount *fmp = data;
43 if (!fuse_cdevpriv_close(fmp))
48 fuse_device_open(struct dev_open_args *ap)
50 struct fuse_mount *fmp;
52 fmp = kmalloc(sizeof(*fmp), M_TEMP, M_WAITOK | M_ZERO);
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);
63 fuse_device_close(struct dev_close_args *ap)
65 struct fuse_mount *fmp;
68 error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
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);
81 fuse_cdevpriv_close(struct fuse_mount *fmp)
84 fuse_print("/dev/%s not associated with FUSE mount\n",
89 mtx_lock(&fmp->mnt_lock);
90 if (fuse_mount_kill(fmp) == -1)
91 KNOTE(&fmp->kq.ki_note, 0);
93 mtx_unlock(&fmp->mnt_lock);
98 /* Call with ->ipc_lock held. */
100 fuse_device_clear(struct fuse_mount *fmp)
102 struct fuse_ipc *fip;
104 while ((fip = TAILQ_FIRST(&fmp->request_head)))
105 TAILQ_REMOVE(&fmp->request_head, fip, request_entry);
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))
115 fuse_device_read(struct dev_read_args *ap)
117 struct uio *uio = ap->a_uio;
118 struct fuse_mount *fmp;
119 struct fuse_ipc *fip;
122 error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
126 if (fuse_test_dead(fmp))
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);
139 mtx_unlock(&fmp->ipc_lock);
140 fuse_dbg("error=%d\n", error);
144 TAILQ_REMOVE(&fmp->request_head, fip, request_entry);
145 mtx_unlock(&fmp->ipc_lock);
147 fuse_dbgipc(fip, 0, "");
149 if (uio->uio_resid < fuse_in_size(fip))
152 return uiomove(fuse_in(fip), fuse_in_size(fip), uio);
156 fuse_device_write(struct dev_write_args *ap)
158 struct uio *uio = ap->a_uio;
159 struct fuse_mount *fmp;
160 struct fuse_ipc *fip;
162 struct fuse_in_header *ihd;
163 struct fuse_out_header *ohd;
166 error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
170 if (uio->uio_resid < sizeof(*ohd))
173 fuse_buf_alloc(&fb, uio->uio_resid);
174 error = uiomove(fb.buf, uio->uio_resid, uio);
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);
188 mtx_unlock(&fmp->ipc_lock);
191 fuse_dbg("unique=%ju not found\n", ohd->unique);
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)) {
205 fuse_dbgipc(fip, error, "audit");
207 fuse_dbgipc(fip, 0, "");
209 /* Complete the IPC regardless of above result. */
210 if (fuse_ipc_test_and_set_replied(fip))
216 static void filt_fusedevdetach(struct knote*);
217 static int filt_fusedevread(struct knote*, long);
218 static int filt_fusedevwrite(struct knote*, long);
220 static struct filterops fusedevread_filterops =
222 NULL, filt_fusedevdetach, filt_fusedevread };
223 static struct filterops fusedevwrite_filterops =
225 NULL, filt_fusedevdetach, filt_fusedevwrite };
228 fuse_device_kqfilter(struct dev_kqfilter_args *ap)
230 struct knote *kn = ap->a_kn;
232 struct fuse_mount *fmp;
235 error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
237 ap->a_result = error;
243 switch (kn->kn_filter) {
245 kn->kn_fop = &fusedevread_filterops;
246 kn->kn_hook = (caddr_t)fmp;
249 kn->kn_fop = &fusedevwrite_filterops;
250 kn->kn_hook = (caddr_t)fmp;
253 ap->a_result = EOPNOTSUPP;
257 klist = &fmp->kq.ki_note;
258 knote_insert(klist, kn);
264 filt_fusedevdetach(struct knote *kn)
266 struct fuse_mount *fmp = (struct fuse_mount*)kn->kn_hook;
267 struct klist *klist = &fmp->kq.ki_note;
269 knote_remove(klist, kn);
273 filt_fusedevread(struct knote *kn, long hint)
275 struct fuse_mount *fmp = (struct fuse_mount*)kn->kn_hook;
278 mtx_lock(&fmp->ipc_lock);
279 if (!TAILQ_EMPTY(&fmp->request_head))
281 mtx_unlock(&fmp->ipc_lock);
287 filt_fusedevwrite(struct knote *kn, long hint)
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,
302 fuse_device_init(void)
304 fuse_dev = make_dev(&fuse_device_cdevsw, 0, UID_ROOT, GID_OPERATOR,
305 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, "fuse");
314 fuse_device_cleanup(void)
317 destroy_dev(fuse_dev);