nrelease - fix/improve livecd
[dragonfly.git] / sys / vfs / fuse / fuse_device.c
blobc1dde3bec3be4aaf6ad2b3fe729c5cb81260c97d
1 /*-
2 * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org>
3 * Copyright (c) 2019 The DragonFly Project
4 * All rights reserved.
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.
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.
28 #include "fuse.h"
30 #include <sys/conf.h>
31 #include <sys/device.h>
32 #include <sys/devfs.h>
33 #include <sys/uio.h>
35 static int fuse_cdevpriv_close(struct fuse_mount*);
36 static struct cdev *fuse_dev;
38 static void
39 fuse_cdevpriv_dtor(void *data)
41 struct fuse_mount *fmp = data;
43 if (!fuse_cdevpriv_close(fmp))
44 fuse_mount_free(fmp);
47 static int
48 fuse_device_open(struct dev_open_args *ap)
50 struct fuse_mount *fmp;
51 struct file *fp = ap->a_fpp ? *ap->a_fpp : NULL;
53 fmp = kmalloc(sizeof(*fmp), M_TEMP, M_WAITOK | M_ZERO);
54 KKASSERT(fmp);
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);
60 return 0;
63 static int
64 fuse_device_close(struct dev_close_args *ap)
66 struct fuse_mount *fmp;
67 int error;
69 error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
70 if (error)
71 return error;
72 KKASSERT(fmp);
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);
78 return 0;
81 static int
82 fuse_cdevpriv_close(struct fuse_mount *fmp)
84 if (!fmp->devvp) {
85 fuse_print("/dev/%s not associated with FUSE mount\n",
86 fuse_dev->si_name);
87 return ENODEV;
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);
96 return 0;
99 /* Call with ->ipc_lock held. */
100 static void
101 fuse_device_clear(struct fuse_mount *fmp)
103 struct fuse_ipc *fip;
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);
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);
118 static int
119 fuse_device_read(struct dev_read_args *ap)
121 struct uio *uio = ap->a_uio;
122 struct fuse_mount *fmp;
123 struct fuse_ipc *fip;
124 int error;
126 error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
127 if (error)
128 return error;
130 if (fuse_test_dead(fmp))
131 return ENOTCONN;
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;
142 if (error) {
143 mtx_unlock(&fmp->ipc_lock);
144 fuse_dbg("error=%d\n", error);
145 return error;
148 TAILQ_REMOVE(&fmp->request_head, fip, request_entry);
149 mtx_unlock(&fmp->ipc_lock);
151 fuse_dbgipc(fip, 0, "");
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);
158 if (atomic_swap_int(&fip->sent, 1) == -1)
159 wakeup(fip);
161 return error;
164 static int
165 fuse_device_write(struct dev_write_args *ap)
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;
175 error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
176 if (error)
177 return error;
179 if (uio->uio_resid < sizeof(*ohd))
180 return EILSEQ;
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;
188 ohd = fb.buf;
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;
197 mtx_unlock(&fmp->ipc_lock);
199 if (!fip) {
200 fuse_dbg("unique=%ju not found\n", ohd->unique);
201 fuse_buf_free(&fb);
202 return ENOMSG;
205 fip->reply = fb;
206 ihd = fuse_in(fip);
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, "");
218 /* Complete the IPC regardless of above result. */
219 if (fuse_ipc_test_and_set_replied(fip))
220 wakeup(fip);
222 return error;
225 static void filt_fusedevdetach(struct knote*);
226 static int filt_fusedevread(struct knote*, long);
227 static int filt_fusedevwrite(struct knote*, long);
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 };
236 static int
237 fuse_device_kqfilter(struct dev_kqfilter_args *ap)
239 struct knote *kn = ap->a_kn;
240 struct klist *klist;
241 struct fuse_mount *fmp;
242 int error;
244 error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp);
245 if (error) {
246 ap->a_result = error;
247 return 0;
250 ap->a_result = 0;
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;
266 klist = &fmp->kq.ki_note;
267 knote_insert(klist, kn);
269 return 0;
272 static void
273 filt_fusedevdetach(struct knote *kn)
275 struct fuse_mount *fmp = (struct fuse_mount*)kn->kn_hook;
276 struct klist *klist = &fmp->kq.ki_note;
278 knote_remove(klist, kn);
281 static int
282 filt_fusedevread(struct knote *kn, long hint)
284 struct fuse_mount *fmp = (struct fuse_mount*)kn->kn_hook;
285 int ready = 0;
287 mtx_lock(&fmp->ipc_lock);
288 if (!TAILQ_EMPTY(&fmp->request_head))
289 ready = 1;
290 mtx_unlock(&fmp->ipc_lock);
292 return ready;
295 static int
296 filt_fusedevwrite(struct knote *kn, long hint)
298 return 1;
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,
311 fuse_device_init(void)
313 fuse_dev = make_dev(&fuse_device_cdevsw, 0, UID_ROOT, GID_OPERATOR,
314 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, "fuse");
316 if (!fuse_dev)
317 return ENOMEM;
319 return 0;
322 void
323 fuse_device_cleanup(void)
325 KKASSERT(fuse_dev);
326 destroy_dev(fuse_dev);