| 1 | /* $NetBSD: puffs_node.c,v 1.37 2016/08/20 12:37:08 hannken Exp $ */ |
| 2 | |
| 3 | /* |
| 4 | * Copyright (c) 2005, 2006, 2007 Antti Kantee. All Rights Reserved. |
| 5 | * |
| 6 | * Development of this software was supported by the |
| 7 | * Google Summer of Code program, the Ulla Tuominen Foundation |
| 8 | * and the Finnish Cultural Foundation. |
| 9 | * |
| 10 | * Redistribution and use in source and binary forms, with or without |
| 11 | * modification, are permitted provided that the following conditions |
| 12 | * are met: |
| 13 | * 1. Redistributions of source code must retain the above copyright |
| 14 | * notice, this list of conditions and the following disclaimer. |
| 15 | * 2. Redistributions in binary form must reproduce the above copyright |
| 16 | * notice, this list of conditions and the following disclaimer in the |
| 17 | * documentation and/or other materials provided with the distribution. |
| 18 | * |
| 19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS |
| 20 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 22 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 29 | * SUCH DAMAGE. |
| 30 | */ |
| 31 | |
| 32 | #include <sys/cdefs.h> |
| 33 | __KERNEL_RCSID(0, "$NetBSD: puffs_node.c,v 1.37 2016/08/20 12:37:08 hannken Exp $" ); |
| 34 | |
| 35 | #include <sys/param.h> |
| 36 | #include <sys/hash.h> |
| 37 | #include <sys/kmem.h> |
| 38 | #include <sys/mount.h> |
| 39 | #include <sys/namei.h> |
| 40 | #include <sys/vnode.h> |
| 41 | |
| 42 | #include <uvm/uvm.h> |
| 43 | |
| 44 | #include <fs/puffs/puffs_msgif.h> |
| 45 | #include <fs/puffs/puffs_sys.h> |
| 46 | |
| 47 | #include <miscfs/genfs/genfs_node.h> |
| 48 | #include <miscfs/specfs/specdev.h> |
| 49 | |
| 50 | struct pool puffs_pnpool; |
| 51 | struct pool puffs_vapool; |
| 52 | |
| 53 | /* |
| 54 | * Grab a vnode, intialize all the puffs-dependent stuff. |
| 55 | */ |
| 56 | static int |
| 57 | puffs_getvnode1(struct mount *mp, puffs_cookie_t ck, enum vtype type, |
| 58 | voff_t vsize, dev_t rdev, bool may_exist, struct vnode **vpp) |
| 59 | { |
| 60 | struct puffs_mount *pmp; |
| 61 | struct vnode *vp; |
| 62 | struct puffs_node *pnode; |
| 63 | int error; |
| 64 | |
| 65 | pmp = MPTOPUFFSMP(mp); |
| 66 | |
| 67 | if (type <= VNON || type >= VBAD) { |
| 68 | puffs_senderr(pmp, PUFFS_ERR_MAKENODE, EINVAL, |
| 69 | "bad node type" , ck); |
| 70 | return EPROTO; |
| 71 | } |
| 72 | if (vsize == VSIZENOTSET) { |
| 73 | puffs_senderr(pmp, PUFFS_ERR_MAKENODE, EINVAL, |
| 74 | "VSIZENOTSET is not a valid size" , ck); |
| 75 | return EPROTO; |
| 76 | } |
| 77 | |
| 78 | for (;;) { |
| 79 | error = vcache_get(mp, &ck, sizeof(ck), &vp); |
| 80 | if (error) |
| 81 | return error; |
| 82 | mutex_enter(vp->v_interlock); |
| 83 | pnode = VPTOPP(vp); |
| 84 | if (pnode != NULL) |
| 85 | break; |
| 86 | mutex_exit(vp->v_interlock); |
| 87 | vrele(vp); |
| 88 | } |
| 89 | mutex_enter(&pnode->pn_mtx); |
| 90 | mutex_exit(vp->v_interlock); |
| 91 | |
| 92 | /* |
| 93 | * Release and error out if caller wants a fresh vnode. |
| 94 | */ |
| 95 | if (vp->v_type != VNON && ! may_exist) { |
| 96 | mutex_exit(&pnode->pn_mtx); |
| 97 | vrele(vp); |
| 98 | return EEXIST; |
| 99 | } |
| 100 | |
| 101 | *vpp = vp; |
| 102 | |
| 103 | /* |
| 104 | * If fully initialized were done. |
| 105 | */ |
| 106 | if (vp->v_type != VNON) { |
| 107 | mutex_exit(&pnode->pn_mtx); |
| 108 | return 0; |
| 109 | } |
| 110 | |
| 111 | /* |
| 112 | * Set type and finalize the initialisation. |
| 113 | */ |
| 114 | vp->v_type = type; |
| 115 | if (type == VCHR || type == VBLK) { |
| 116 | vp->v_op = puffs_specop_p; |
| 117 | spec_node_init(vp, rdev); |
| 118 | } else if (type == VFIFO) { |
| 119 | vp->v_op = puffs_fifoop_p; |
| 120 | } else if (vp->v_type == VREG) { |
| 121 | uvm_vnp_setsize(vp, vsize); |
| 122 | } |
| 123 | |
| 124 | pnode->pn_serversize = vsize; |
| 125 | |
| 126 | DPRINTF(("new vnode at %p, pnode %p, cookie %p\n" , vp, |
| 127 | pnode, pnode->pn_cookie)); |
| 128 | |
| 129 | mutex_exit(&pnode->pn_mtx); |
| 130 | |
| 131 | return 0; |
| 132 | } |
| 133 | |
| 134 | int |
| 135 | puffs_getvnode(struct mount *mp, puffs_cookie_t ck, enum vtype type, |
| 136 | voff_t vsize, dev_t rdev, struct vnode **vpp) |
| 137 | { |
| 138 | |
| 139 | return puffs_getvnode1(mp, ck, type, vsize, rdev, true, vpp); |
| 140 | } |
| 141 | |
| 142 | /* new node creating for creative vop ops (create, symlink, mkdir, mknod) */ |
| 143 | int |
| 144 | puffs_newnode(struct mount *mp, struct vnode *dvp, struct vnode **vpp, |
| 145 | puffs_cookie_t ck, struct componentname *cnp, |
| 146 | enum vtype type, dev_t rdev) |
| 147 | { |
| 148 | struct puffs_mount *pmp = MPTOPUFFSMP(mp); |
| 149 | int error; |
| 150 | |
| 151 | /* userspace probably has this as a NULL op */ |
| 152 | if (ck == NULL) |
| 153 | return EOPNOTSUPP; |
| 154 | |
| 155 | /* |
| 156 | * Check for previous node with the same designation. |
| 157 | * Explicitly check the root node cookie, since it might be |
| 158 | * reclaimed from the kernel when this check is made. |
| 159 | */ |
| 160 | if (ck == pmp->pmp_root_cookie) { |
| 161 | puffs_senderr(pmp, PUFFS_ERR_MAKENODE, EEXIST, |
| 162 | "cookie exists" , ck); |
| 163 | return EPROTO; |
| 164 | } |
| 165 | |
| 166 | KASSERT(curlwp != uvm.pagedaemon_lwp); |
| 167 | |
| 168 | error = puffs_getvnode1(dvp->v_mount, ck, type, 0, rdev, false, vpp); |
| 169 | if (error) { |
| 170 | if (error == EEXIST) { |
| 171 | puffs_senderr(pmp, PUFFS_ERR_MAKENODE, EEXIST, |
| 172 | "cookie exists" , ck); |
| 173 | error = EPROTO; |
| 174 | } |
| 175 | return error; |
| 176 | } |
| 177 | |
| 178 | if (PUFFS_USE_NAMECACHE(pmp)) |
| 179 | cache_enter(dvp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, |
| 180 | cnp->cn_flags); |
| 181 | |
| 182 | puffs_updatenode(VPTOPP(dvp), PUFFS_UPDATECTIME|PUFFS_UPDATEMTIME, 0); |
| 183 | |
| 184 | return 0; |
| 185 | } |
| 186 | |
| 187 | void |
| 188 | puffs_putvnode(struct vnode *vp) |
| 189 | { |
| 190 | struct puffs_node *pnode; |
| 191 | |
| 192 | pnode = VPTOPP(vp); |
| 193 | |
| 194 | KASSERT(vp->v_tag == VT_PUFFS); |
| 195 | |
| 196 | genfs_node_destroy(vp); |
| 197 | |
| 198 | /* |
| 199 | * To interlock with puffs_getvnode1(). |
| 200 | */ |
| 201 | mutex_enter(vp->v_interlock); |
| 202 | vp->v_data = NULL; |
| 203 | mutex_exit(vp->v_interlock); |
| 204 | puffs_releasenode(pnode); |
| 205 | } |
| 206 | |
| 207 | /* |
| 208 | * Make sure root vnode exists and reference it. Does NOT lock. |
| 209 | */ |
| 210 | static int |
| 211 | puffs_makeroot(struct puffs_mount *pmp) |
| 212 | { |
| 213 | struct vnode *vp; |
| 214 | int rv; |
| 215 | |
| 216 | /* |
| 217 | * pmp_lock must be held if vref()'ing or vrele()'ing the |
| 218 | * root vnode. the latter is controlled by puffs_inactive(). |
| 219 | * |
| 220 | * pmp_root is set here and cleared in puffs_reclaim(). |
| 221 | */ |
| 222 | |
| 223 | rv = puffs_getvnode(pmp->pmp_mp, pmp->pmp_root_cookie, |
| 224 | pmp->pmp_root_vtype, pmp->pmp_root_vsize, pmp->pmp_root_rdev, &vp); |
| 225 | if (rv != 0) |
| 226 | return rv; |
| 227 | |
| 228 | mutex_enter(&pmp->pmp_lock); |
| 229 | if (pmp->pmp_root == NULL) |
| 230 | pmp->pmp_root = vp; |
| 231 | mutex_exit(&pmp->pmp_lock); |
| 232 | |
| 233 | return 0; |
| 234 | } |
| 235 | |
| 236 | /* |
| 237 | * Locate the in-kernel vnode based on the cookie received given |
| 238 | * from userspace. |
| 239 | * |
| 240 | * returns 0 on success. otherwise returns an errno or PUFFS_NOSUCHCOOKIE. |
| 241 | * |
| 242 | * returns PUFFS_NOSUCHCOOKIE if no vnode for the cookie is found. |
| 243 | */ |
| 244 | int |
| 245 | puffs_cookie2vnode(struct puffs_mount *pmp, puffs_cookie_t ck, |
| 246 | struct vnode **vpp) |
| 247 | { |
| 248 | int rv; |
| 249 | |
| 250 | /* |
| 251 | * Handle root in a special manner, since we want to make sure |
| 252 | * pmp_root is properly set. |
| 253 | */ |
| 254 | if (ck == pmp->pmp_root_cookie) { |
| 255 | if ((rv = puffs_makeroot(pmp))) |
| 256 | return rv; |
| 257 | *vpp = pmp->pmp_root; |
| 258 | return 0; |
| 259 | } |
| 260 | |
| 261 | rv = vcache_get(PMPTOMP(pmp), &ck, sizeof(ck), vpp); |
| 262 | if (rv != 0) |
| 263 | return rv; |
| 264 | mutex_enter((*vpp)->v_interlock); |
| 265 | if ((*vpp)->v_type == VNON) { |
| 266 | mutex_exit((*vpp)->v_interlock); |
| 267 | /* XXX vrele() calls VOP_INACTIVE() with VNON node */ |
| 268 | vrele(*vpp); |
| 269 | *vpp = NULL; |
| 270 | return PUFFS_NOSUCHCOOKIE; |
| 271 | } |
| 272 | mutex_exit((*vpp)->v_interlock); |
| 273 | |
| 274 | return 0; |
| 275 | } |
| 276 | |
| 277 | void |
| 278 | puffs_updatenode(struct puffs_node *pn, int flags, voff_t size) |
| 279 | { |
| 280 | struct timespec ts; |
| 281 | |
| 282 | if (flags == 0) |
| 283 | return; |
| 284 | |
| 285 | nanotime(&ts); |
| 286 | |
| 287 | if (flags & PUFFS_UPDATEATIME) { |
| 288 | pn->pn_mc_atime = ts; |
| 289 | pn->pn_stat |= PNODE_METACACHE_ATIME; |
| 290 | } |
| 291 | if (flags & PUFFS_UPDATECTIME) { |
| 292 | pn->pn_mc_ctime = ts; |
| 293 | pn->pn_stat |= PNODE_METACACHE_CTIME; |
| 294 | } |
| 295 | if (flags & PUFFS_UPDATEMTIME) { |
| 296 | pn->pn_mc_mtime = ts; |
| 297 | pn->pn_stat |= PNODE_METACACHE_MTIME; |
| 298 | } |
| 299 | if (flags & PUFFS_UPDATESIZE) { |
| 300 | pn->pn_mc_size = size; |
| 301 | pn->pn_stat |= PNODE_METACACHE_SIZE; |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | /* |
| 306 | * Add reference to node. |
| 307 | * mutex held on entry and return |
| 308 | */ |
| 309 | void |
| 310 | puffs_referencenode(struct puffs_node *pn) |
| 311 | { |
| 312 | |
| 313 | KASSERT(mutex_owned(&pn->pn_mtx)); |
| 314 | pn->pn_refcount++; |
| 315 | } |
| 316 | |
| 317 | /* |
| 318 | * Release pnode structure which dealing with references to the |
| 319 | * puffs_node instead of the vnode. Can't use vref()/vrele() on |
| 320 | * the vnode there, since that causes the lovely VOP_INACTIVE(), |
| 321 | * which in turn causes the lovely deadlock when called by the one |
| 322 | * who is supposed to handle it. |
| 323 | */ |
| 324 | void |
| 325 | puffs_releasenode(struct puffs_node *pn) |
| 326 | { |
| 327 | |
| 328 | mutex_enter(&pn->pn_mtx); |
| 329 | if (--pn->pn_refcount == 0) { |
| 330 | mutex_exit(&pn->pn_mtx); |
| 331 | mutex_destroy(&pn->pn_mtx); |
| 332 | mutex_destroy(&pn->pn_sizemtx); |
| 333 | seldestroy(&pn->pn_sel); |
| 334 | if (pn->pn_va_cache != NULL) |
| 335 | pool_put(&puffs_vapool, pn->pn_va_cache); |
| 336 | pool_put(&puffs_pnpool, pn); |
| 337 | } else { |
| 338 | mutex_exit(&pn->pn_mtx); |
| 339 | } |
| 340 | } |
| 341 | |