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 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 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 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 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 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 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 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 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 323 fuse_device_cleanup(void) 324 { 325 KKASSERT(fuse_dev); 326 destroy_dev(fuse_dev); 327 } 328