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 } 319