/* * Find root of lofs mount. */ static int lo_root(struct vfs *vfsp, struct vnode **vpp) { *vpp = vtoli(vfsp)->li_rootvp; #ifdef LODEBUG lo_dprint(4, "lo_root(0x%p) = %p\n", vfsp, *vpp); #endif /* * If the root of the filesystem is a special file, return the specvp * version of the vnode. We don't save the specvp vnode in our * hashtable since that's exclusively for lnodes. */ if (IS_DEVVP(*vpp)) { struct vnode *svp; svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, kcred); if (svp == NULL) return (ENOSYS); *vpp = svp; } else { VN_HOLD(*vpp); } return (0); }
/* * Undo loopback mount */ static int lo_unmount(struct vfs *vfsp, int flag, struct cred *cr) { struct loinfo *li; if (secpolicy_fs_unmount(cr, vfsp) != 0) return (EPERM); /* * Forced unmount is not supported by this file system * and thus, ENOTSUP, is being returned. */ if (flag & MS_FORCE) return (ENOTSUP); li = vtoli(vfsp); #ifdef LODEBUG lo_dprint(4, "lo_unmount(%p) li %p\n", vfsp, li); #endif if (li->li_refct != 1 || li->li_rootvp->v_count != 1) { #ifdef LODEBUG lo_dprint(4, "refct %d v_ct %d\n", li->li_refct, li->li_rootvp->v_count); #endif return (EBUSY); } VN_RELE(li->li_rootvp); return (0); }
/* * Free mount-specific data. */ static void lo_freevfs(struct vfs *vfsp) { struct loinfo *li = vtoli(vfsp); ldestroy(li); kmem_free(li, sizeof (struct loinfo)); }
/* * Find real vfs given loopback vfs */ struct vfs * lo_realvfs(struct vfs *vfsp, struct vnode **realrootvpp) { struct loinfo *li = vtoli(vfsp); struct lfsnode *lfs; ASSERT(li->li_refct > 0); if (vfsp == li->li_mountvfs) { if (realrootvpp != NULL) *realrootvpp = vtol(li->li_rootvp)->lo_vp; return (li->li_realvfs); } mutex_enter(&li->li_lfslock); for (lfs = li->li_lfs; lfs != NULL; lfs = lfs->lfs_next) { if (vfsp == &lfs->lfs_vfs) { if (realrootvpp != NULL) *realrootvpp = lfs->lfs_realrootvp; mutex_exit(&li->li_lfslock); return (lfs->lfs_realvfs); } } panic("lo_realvfs"); /*NOTREACHED*/ }
/* * Remove a lnode from the table */ void freelonode(lnode_t *lp) { lnode_t *lt; lnode_t *ltprev = NULL; struct lfsnode *lfs, *nextlfs; struct vfs *vfsp; struct vnode *vp = ltov(lp); struct vnode *realvp = realvp(vp); struct loinfo *li = vtoli(vp->v_vfsp); #ifdef LODEBUG lo_dprint(4, "freelonode lp %p hash %d\n", lp, ltablehash(lp->lo_vp, li)); #endif TABLE_LOCK_ENTER(lp->lo_vp, li); mutex_enter(&vp->v_lock); if (vp->v_count > 1) { vp->v_count--; /* release our hold from vn_rele */ mutex_exit(&vp->v_lock); TABLE_LOCK_EXIT(lp->lo_vp, li); return; } mutex_exit(&vp->v_lock); for (lt = TABLE_BUCKET(lp->lo_vp, li); lt != NULL; ltprev = lt, lt = lt->lo_next) { if (lt == lp) { #ifdef LODEBUG lo_dprint(4, "freeing %p, vfsp %p\n", vp, vp->v_vfsp); #endif atomic_dec_32(&li->li_refct); vfsp = vp->v_vfsp; vn_invalid(vp); if (vfsp != li->li_mountvfs) { mutex_enter(&li->li_lfslock); /* * Check for unused lfs */ lfs = li->li_lfs; while (lfs != NULL) { nextlfs = lfs->lfs_next; if (vfsp == &lfs->lfs_vfs) { lfs_rele(lfs, li); break; } if (lfs->lfs_vfs.vfs_count == 1) { /* * Lfs is idle */ freelfsnode(lfs, li); } lfs = nextlfs; } mutex_exit(&li->li_lfslock); } if (ltprev == NULL) { TABLE_BUCKET(lt->lo_vp, li) = lt->lo_next; } else { ltprev->lo_next = lt->lo_next; } TABLE_COUNT(lt->lo_vp, li)--; TABLE_LOCK_EXIT(lt->lo_vp, li); kmem_cache_free(lnode_cache, lt); vn_free(vp); VN_RELE(realvp); return; } } panic("freelonode"); /*NOTREACHED*/ }