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 108 while ((fip = TAILQ_FIRST(&fmp->reply_head))) { 109 TAILQ_REMOVE(&fmp->reply_head, fip, reply_entry); 110 if (fuse_ipc_test_and_set_replied(fip)) 111 wakeup(fip); 112 } 113 } 114 115 static int 116 fuse_device_read(struct dev_read_args *ap) 117 { 118 struct uio *uio = ap->a_uio; 119 struct fuse_mount *fmp; 120 struct fuse_ipc *fip; 121 int error; 122 123 error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp); 124 if (error) 125 return error; 126 127 if (fuse_test_dead(fmp)) 128 return ENOTCONN; 129 130 mtx_lock(&fmp->ipc_lock); 131 while (!(fip = TAILQ_FIRST(&fmp->request_head))) { 132 error = mtxsleep(fmp, &fmp->ipc_lock, PCATCH, "ftxc", 0); 133 if (fuse_test_dead(fmp)) { 134 fuse_device_clear(fmp); 135 mtx_unlock(&fmp->ipc_lock); 136 fuse_dbg("error=%d dead\n", error); 137 return ENOTCONN; 138 } 139 if (error) { 140 mtx_unlock(&fmp->ipc_lock); 141 fuse_dbg("error=%d\n", error); 142 return error; 143 } 144 } 145 TAILQ_REMOVE(&fmp->request_head, fip, request_entry); 146 mtx_unlock(&fmp->ipc_lock); 147 148 fuse_dbgipc(fip, 0, ""); 149 150 if (uio->uio_resid < fuse_in_size(fip)) 151 return EILSEQ; 152 153 return uiomove(fuse_in(fip), fuse_in_size(fip), uio); 154 } 155 156 static int 157 fuse_device_write(struct dev_write_args *ap) 158 { 159 struct uio *uio = ap->a_uio; 160 struct fuse_mount *fmp; 161 struct fuse_ipc *fip; 162 struct fuse_buf fb; 163 struct fuse_in_header *ihd; 164 struct fuse_out_header *ohd; 165 int error; 166 167 error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp); 168 if (error) 169 return error; 170 171 if (uio->uio_resid < sizeof(*ohd)) 172 return EILSEQ; 173 174 fuse_buf_alloc(&fb, uio->uio_resid); 175 error = uiomove(fb.buf, uio->uio_resid, uio); 176 if (error) { 177 fuse_buf_free(&fb); 178 return error; 179 } 180 ohd = fb.buf; 181 182 mtx_lock(&fmp->ipc_lock); 183 TAILQ_FOREACH(fip, &fmp->reply_head, reply_entry) { 184 if (fip->unique == ohd->unique) { 185 TAILQ_REMOVE(&fmp->reply_head, fip, reply_entry); 186 break; 187 } 188 } 189 mtx_unlock(&fmp->ipc_lock); 190 191 if (!fip) { 192 fuse_dbg("unique=%ju not found\n", ohd->unique); 193 fuse_buf_free(&fb); 194 return ENOMSG; 195 } 196 197 fip->reply = fb; 198 ihd = fuse_in(fip); 199 200 /* Non zero ohd->error is not /dev/fuse write error. */ 201 if (ohd->error == -ENOSYS) { 202 fuse_set_nosys(fmp, ihd->opcode); 203 fuse_dbgipc(fip, ohd->error, "ENOSYS"); 204 } else if (!ohd->error && fuse_audit_length(ihd, ohd)) { 205 error = EPROTO; 206 fuse_dbgipc(fip, error, "audit"); 207 } else 208 fuse_dbgipc(fip, 0, ""); 209 210 /* Complete the IPC regardless of above result. */ 211 if (fuse_ipc_test_and_set_replied(fip)) 212 wakeup(fip); 213 214 return error; 215 } 216 217 static void filt_fusedevdetach(struct knote*); 218 static int filt_fusedevread(struct knote*, long); 219 static int filt_fusedevwrite(struct knote*, long); 220 221 static struct filterops fusedevread_filterops = 222 { FILTEROP_ISFD, 223 NULL, filt_fusedevdetach, filt_fusedevread }; 224 static struct filterops fusedevwrite_filterops = 225 { FILTEROP_ISFD, 226 NULL, filt_fusedevdetach, filt_fusedevwrite }; 227 228 static int 229 fuse_device_kqfilter(struct dev_kqfilter_args *ap) 230 { 231 struct knote *kn = ap->a_kn; 232 struct klist *klist; 233 struct fuse_mount *fmp; 234 int error; 235 236 error = devfs_get_cdevpriv(ap->a_fp, (void**)&fmp); 237 if (error) { 238 ap->a_result = error; 239 return 0; 240 } 241 242 ap->a_result = 0; 243 244 switch (kn->kn_filter) { 245 case EVFILT_READ: 246 kn->kn_fop = &fusedevread_filterops; 247 kn->kn_hook = (caddr_t)fmp; 248 break; 249 case EVFILT_WRITE: 250 kn->kn_fop = &fusedevwrite_filterops; 251 kn->kn_hook = (caddr_t)fmp; 252 break; 253 default: 254 ap->a_result = EOPNOTSUPP; 255 return 0; 256 } 257 258 klist = &fmp->kq.ki_note; 259 knote_insert(klist, kn); 260 261 return 0; 262 } 263 264 static void 265 filt_fusedevdetach(struct knote *kn) 266 { 267 struct fuse_mount *fmp = (struct fuse_mount*)kn->kn_hook; 268 struct klist *klist = &fmp->kq.ki_note; 269 270 knote_remove(klist, kn); 271 } 272 273 static int 274 filt_fusedevread(struct knote *kn, long hint) 275 { 276 struct fuse_mount *fmp = (struct fuse_mount*)kn->kn_hook; 277 int ready = 0; 278 279 mtx_lock(&fmp->ipc_lock); 280 if (!TAILQ_EMPTY(&fmp->request_head)) 281 ready = 1; 282 mtx_unlock(&fmp->ipc_lock); 283 284 return ready; 285 } 286 287 static int 288 filt_fusedevwrite(struct knote *kn, long hint) 289 { 290 return 1; 291 } 292 293 static struct dev_ops fuse_device_cdevsw = { 294 { "fuse", 0, D_MPSAFE, }, 295 .d_open = fuse_device_open, 296 .d_close = fuse_device_close, 297 .d_read = fuse_device_read, 298 .d_write = fuse_device_write, 299 .d_kqfilter = fuse_device_kqfilter, 300 }; 301 302 int 303 fuse_device_init(void) 304 { 305 fuse_dev = make_dev(&fuse_device_cdevsw, 0, UID_ROOT, GID_OPERATOR, 306 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, "fuse"); 307 308 if (!fuse_dev) 309 return ENOMEM; 310 311 return 0; 312 } 313 314 void 315 fuse_device_cleanup(void) 316 { 317 KKASSERT(fuse_dev); 318 destroy_dev(fuse_dev); 319 } 320