sys/vfs/fuse: Cleanup (use TAILQ_FOREACH())
[dragonfly.git] / sys / vfs / fuse / fuse_device.c
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 }