xref: /dragonfly/sys/vfs/fuse/fuse_device.c (revision 1a8e5e4cf0cfbe16676810f7edca73f98bb9b8f5)
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