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 static MALLOC_DEFINE(M_FUSE_NODE, "fuse_node", "FUSE node"); 31 32 static struct objcache *fuse_node_objcache = NULL; 33 static struct objcache_malloc_args fuse_node_args = { 34 sizeof(struct fuse_node), M_FUSE_NODE, 35 }; 36 37 static MALLOC_DEFINE(M_FUSE_DENT, "fuse_dent", "FUSE dent"); 38 39 static struct objcache *fuse_dent_objcache = NULL; 40 static struct objcache_malloc_args fuse_dent_args = { 41 sizeof(struct fuse_dent), M_FUSE_DENT, 42 }; 43 44 static int 45 fuse_dent_cmp(struct fuse_dent *p1, struct fuse_dent *p2) 46 { 47 return strcmp(p1->name, p2->name); 48 } 49 50 RB_PROTOTYPE_STATIC(fuse_dent_tree, fuse_dent, entry, fuse_dent_cmp); 51 RB_GENERATE_STATIC(fuse_dent_tree, fuse_dent, dent_entry, fuse_dent_cmp); 52 53 void 54 fuse_node_new(struct fuse_mount *fmp, uint64_t ino, enum vtype vtyp, 55 struct fuse_node **fnpp) 56 { 57 struct fuse_node *fnp; 58 59 fnp = objcache_get(fuse_node_objcache, M_WAITOK); 60 KKASSERT(fnp); 61 62 memset(fnp, 0, sizeof(*fnp)); 63 fnp->vp = NULL; 64 fnp->fmp = fmp; 65 fnp->pfnp = NULL; 66 67 mtx_init(&fnp->node_lock, "fuse_node_lock"); 68 RB_INIT(&fnp->dent_head); 69 70 fnp->ino = ino; 71 fnp->type = vtyp; 72 fnp->nlink = 0; 73 fnp->size = 0; 74 fnp->nlookup = 0; 75 fnp->fh = 0; 76 fnp->closed = false; 77 78 *fnpp = fnp; 79 KKASSERT(*fnpp); 80 } 81 82 void 83 fuse_node_free(struct fuse_node *fnp) 84 { 85 struct fuse_node *dfnp = fnp->pfnp; 86 struct fuse_dent *fep; 87 88 fuse_dbg("free ino=%ju\n", fnp->ino); 89 90 if (dfnp) { 91 KKASSERT(dfnp->type == VDIR); 92 mtx_lock(&dfnp->node_lock); 93 RB_FOREACH(fep, fuse_dent_tree, &dfnp->dent_head) { 94 if (fep->fnp == fnp) { 95 fuse_dent_detach(dfnp, fep); 96 fuse_dent_free(fep); 97 break; 98 } 99 } 100 mtx_unlock(&dfnp->node_lock); 101 } 102 103 mtx_lock(&fnp->node_lock); 104 if (fnp->type == VDIR) { 105 while ((fep = RB_ROOT(&fnp->dent_head))) { 106 fuse_dent_detach(fnp, fep); 107 fuse_dent_free(fep); 108 } 109 } 110 fnp->vp->v_data = NULL; 111 fnp->vp = NULL; 112 fnp->nlink = -123; /* debug */ 113 mtx_unlock(&fnp->node_lock); 114 115 objcache_put(fuse_node_objcache, fnp); 116 } 117 118 void 119 fuse_dent_new(struct fuse_node *fnp, const char *name, int namelen, 120 struct fuse_dent **fepp) 121 { 122 struct fuse_dent *fep; 123 124 fep = objcache_get(fuse_dent_objcache, M_WAITOK); 125 KKASSERT(fep); 126 127 if (namelen >= 0) 128 fep->name = kstrndup(name, namelen, M_TEMP); 129 else 130 fep->name = kstrdup(name, M_TEMP); 131 KKASSERT(fep->name); 132 fep->fnp = fnp; 133 134 KASSERT(fnp->nlink >= 0, ("new ino=%ju nlink=%d dent=\"%s\"", 135 fnp->ino, fnp->nlink, fep->name)); 136 KKASSERT(fnp->nlink < LINK_MAX); 137 fnp->nlink++; 138 139 *fepp = fep; 140 KKASSERT(*fepp); 141 } 142 143 void 144 fuse_dent_free(struct fuse_dent *fep) 145 { 146 struct fuse_node *fnp = fep->fnp; 147 148 fuse_dbg("free dent=\"%s\"\n", fep->name); 149 150 KASSERT(fnp->nlink > 0, ("free ino=%ju nlink=%d dent=\"%s\"", 151 fnp->ino, fnp->nlink, fep->name)); 152 153 if (fep->name) { 154 kfree(fep->name, M_TEMP); 155 fep->name = NULL; 156 } 157 158 KKASSERT(fnp->nlink <= LINK_MAX); 159 fnp->nlink--; 160 161 fep->fnp = NULL; 162 objcache_put(fuse_dent_objcache, fep); 163 } 164 165 void 166 fuse_dent_attach(struct fuse_node *dfnp, struct fuse_dent *fep) 167 { 168 KKASSERT(dfnp); 169 KKASSERT(dfnp->type == VDIR); 170 KKASSERT(mtx_islocked_ex(&dfnp->node_lock)); 171 172 RB_INSERT(fuse_dent_tree, &dfnp->dent_head, fep); 173 } 174 175 void 176 fuse_dent_detach(struct fuse_node *dfnp, struct fuse_dent *fep) 177 { 178 KKASSERT(dfnp); 179 KKASSERT(dfnp->type == VDIR); 180 KKASSERT(mtx_islocked_ex(&dfnp->node_lock)); 181 182 RB_REMOVE(fuse_dent_tree, &dfnp->dent_head, fep); 183 } 184 185 int 186 fuse_dent_find(struct fuse_node *dfnp, const char *name, int namelen, 187 struct fuse_dent **fepp) 188 { 189 struct fuse_dent *fep, find; 190 int error; 191 192 if (namelen >= 0) 193 find.name = kstrndup(name, namelen, M_TEMP); 194 else 195 find.name = kstrdup(name, M_TEMP); 196 KKASSERT(find.name); 197 198 fep = RB_FIND(fuse_dent_tree, &dfnp->dent_head, &find); 199 if (fep) { 200 error = 0; 201 if (fepp) 202 *fepp = fep; 203 } else { 204 error = ENOENT; 205 fuse_dbg("dent=\"%s\" not found\n", find.name); 206 } 207 208 kfree(find.name, M_TEMP); 209 210 return error; 211 } 212 213 int 214 fuse_alloc_node(struct fuse_node *dfnp, uint64_t ino, const char *name, 215 int namelen, enum vtype vtyp, struct vnode **vpp) 216 { 217 struct fuse_node *fnp = NULL; 218 struct fuse_dent *fep = NULL; 219 int error; 220 221 KKASSERT(dfnp->type == VDIR); 222 if (vtyp == VBLK || vtyp == VCHR || vtyp == VFIFO) 223 return EINVAL; 224 225 mtx_lock(&dfnp->node_lock); 226 error = fuse_dent_find(dfnp, name, namelen, &fep); 227 if (!error) { 228 mtx_unlock(&dfnp->node_lock); 229 return EEXIST; 230 } else if (error == ENOENT) { 231 fuse_node_new(dfnp->fmp, ino, vtyp, &fnp); 232 mtx_lock(&fnp->node_lock); 233 fnp->pfnp = dfnp; 234 fuse_dent_new(fnp, name, namelen, &fep); 235 fuse_dent_attach(dfnp, fep); 236 mtx_unlock(&fnp->node_lock); 237 } else 238 KKASSERT(0); 239 mtx_unlock(&dfnp->node_lock); 240 241 error = fuse_node_vn(fnp, LK_EXCLUSIVE, vpp); 242 if (error) { 243 mtx_lock(&dfnp->node_lock); 244 fuse_dent_detach(dfnp, fep); 245 fuse_dent_free(fep); 246 mtx_unlock(&dfnp->node_lock); 247 fuse_node_free(fnp); 248 return error; 249 } 250 KKASSERT(*vpp); 251 252 fuse_dbg("fnp=%p ino=%ju dent=\"%s\"\n", fnp, fnp->ino, fep->name); 253 254 return 0; 255 } 256 257 int 258 fuse_node_vn(struct fuse_node *fnp, int flags, struct vnode **vpp) 259 { 260 struct mount *mp = fnp->fmp->mp; 261 struct vnode *vp; 262 int error; 263 retry: 264 mtx_lock(&fnp->node_lock); 265 vp = fnp->vp; 266 if (vp) { 267 vhold(vp); 268 mtx_unlock(&fnp->node_lock); 269 270 error = vget(vp, flags | LK_RETRY); 271 if (error) { 272 vdrop(vp); 273 goto retry; 274 } 275 vdrop(vp); 276 *vpp = vp; 277 return 0; 278 } 279 mtx_unlock(&fnp->node_lock); 280 281 error = getnewvnode(VT_FUSE, mp, &vp, VLKTIMEOUT, LK_CANRECURSE); 282 if (error) 283 return error; 284 vp->v_type = fnp->type; 285 vp->v_data = fnp; 286 287 switch (vp->v_type) { 288 case VREG: 289 vinitvmio(vp, fnp->size, FUSE_BLKSIZE, -1); 290 break; 291 case VDIR: 292 break; 293 case VBLK: 294 case VCHR: 295 KKASSERT(0); 296 vp->v_ops = &mp->mnt_vn_spec_ops; 297 addaliasu(vp, umajor(0), uminor(0)); /* XXX CUSE */ 298 break; 299 case VLNK: 300 break; 301 case VSOCK: 302 break; 303 case VFIFO: 304 KKASSERT(0); 305 case VDATABASE: 306 break; 307 default: 308 KKASSERT(0); 309 } 310 311 vx_downgrade(vp); 312 KKASSERT(vn_islocked(vp) == LK_EXCLUSIVE); 313 KASSERT(!fnp->vp, ("lost race")); 314 fnp->vp = vp; 315 *vpp = vp; 316 317 return 0; 318 } 319 320 int 321 fuse_node_truncate(struct fuse_node *fnp, size_t oldsize, size_t newsize) 322 { 323 struct vnode *vp = fnp->vp; 324 int error; 325 326 fuse_dbg("ino=%ju update size %ju -> %ju\n", 327 fnp->ino, oldsize, newsize); 328 329 fnp->attr.va_size = fnp->size = newsize; 330 331 if (newsize < oldsize) 332 error = nvtruncbuf(vp, newsize, FUSE_BLKSIZE, -1, 0); 333 else 334 error = nvextendbuf(vp, oldsize, newsize, FUSE_BLKSIZE, 335 FUSE_BLKSIZE, -1, -1, 0); 336 return error; 337 } 338 339 void 340 fuse_node_init(void) 341 { 342 fuse_node_objcache = objcache_create("fuse_node", 0, 0, 343 NULL, NULL, NULL, 344 objcache_malloc_alloc_zero, objcache_malloc_free, &fuse_node_args); 345 346 fuse_dent_objcache = objcache_create("fuse_dent", 0, 0, 347 NULL, NULL, NULL, 348 objcache_malloc_alloc_zero, objcache_malloc_free, &fuse_dent_args); 349 } 350 351 void 352 fuse_node_cleanup(void) 353 { 354 objcache_destroy(fuse_node_objcache); 355 objcache_destroy(fuse_dent_objcache); 356 } 357