/*- * Copyright (c) 2002 Maxime Henrion * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include static int rootfs_access(struct vop_access_args *); static int rootfs_getattr(struct vop_getattr_args *); static int rootfs_inactive(struct vop_inactive_args *); static int rootfs_lookup(struct vop_lookup_args *); static int rootfs_reclaim(struct vop_reclaim_args *); static int rootfs_allocv(struct rootfs_node *, struct vnode **, struct thread *); static struct rootfs_node rootfs_nodes[] = { { ROOTFS_ROOT, NULL, 0, NULL }, { ROOTFS_DIR, "dev", 3, NULL }, { ROOTFS_DIR, "root", 4, NULL }, { ROOTFS_NONE, NULL, 0, NULL } }; /* * Rootfs is used internally in the kernel when * doing root mounting only. We don't need * to do real access control at this time. */ static int rootfs_access(struct vop_access_args *ap) { int error; error = suser(ap->a_td); KASSERT(error == 0, ("%s: uid is not root", __func__)); return (error); } /* * Translation of a component pathname. */ static int rootfs_lookup(struct vop_lookup_args *ap) { struct vnode *dvp, **vpp; struct componentname *cnp; struct rootfs_node *de; struct thread *td; char *pname; int error, flags, nameiop, namelen; dvp = ap->a_dvp; vpp = ap->a_vpp; cnp = ap->a_cnp; pname = cnp->cn_nameptr; namelen = cnp->cn_namelen; nameiop = cnp->cn_nameiop; td = cnp->cn_thread; flags = cnp->cn_flags; if ((flags & ISLASTCN) && (nameiop != LOOKUP)) panic("nameiop != LOOKUP in rootfs"); /* self */ if (namelen == 1 && *pname == '.') { *vpp = dvp; VREF(dvp); return (0); } /* parent */ if (flags & ISDOTDOT) { de = (struct rootfs_node *)dvp->v_data; if (de->de_type != ROOTFS_DIR) goto notfound; VOP_UNLOCK(dvp, 0, td); /* * Since we support only one level of directories, * we always return the vnode for /. */ de = rootfs_nodes; error = rootfs_allocv(de, vpp, td); if (error) { vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); return (error); } if ((flags & LOCKPARENT) && (flags & ISLASTCN)) error = vn_lock(dvp, LK_EXCLUSIVE, td); if (error) vput(*vpp); return (error); } /* directory entry */ de = (struct rootfs_node *)dvp->v_data; if (de->de_type != ROOTFS_ROOT) goto notfound; de = rootfs_nodes + 1; /* First dir is at offset 1 */ while (de->de_type != ROOTFS_NONE) { if (de->de_namelen == namelen && (bcmp(de->de_name, pname, namelen) == 0)) { error = rootfs_allocv(de, vpp, td); if (error) return (error); if (!(flags & LOCKPARENT) || !(flags & ISLASTCN)) VOP_UNLOCK(dvp, 0, td); return (0); } de++; } notfound: return (ENOENT); } /* * This is for the VOP_GETATTR() call in vfs_nmount(). */ static int rootfs_getattr(struct vop_getattr_args *ap) { vattr_null(ap->a_vap); return (0); } static int rootfs_reclaim(struct vop_reclaim_args *ap) { struct rootfs_node *de; struct vnode *vp; vp = ap->a_vp; de = (struct rootfs_node *)vp->v_data; if (de != NULL) { de->de_vnode = NULL; vp->v_data = NULL; } return (0); } static int rootfs_inactive(struct vop_inactive_args *ap) { struct thread *td; struct vnode *vp; vp = ap->a_vp; td = ap->a_td; VOP_UNLOCK(vp, 0, td); vrecycle(vp, NULL, td); return (0); } static vop_t **rootfs_vnodeop_p; static struct vnodeopv_entry_desc rootfs_vnodeop_entries[] = { { &vop_default_desc, (vop_t *) vop_defaultop }, { &vop_access_desc, (vop_t *) rootfs_access }, { &vop_getattr_desc, (vop_t *) rootfs_getattr }, { &vop_inactive_desc, (vop_t *) rootfs_inactive }, { &vop_islocked_desc, (vop_t *) vop_stdislocked }, { &vop_lock_desc, (vop_t *) vop_stdlock }, { &vop_lookup_desc, (vop_t *) rootfs_lookup }, { &vop_reclaim_desc, (vop_t *) rootfs_reclaim }, { &vop_unlock_desc, (vop_t *) vop_stdunlock }, { NULL, NULL } }; static struct vnodeopv_desc rootfs_vnodeop_opv_desc = { &rootfs_vnodeop_p, rootfs_vnodeop_entries }; VNODEOP_SET(rootfs_vnodeop_opv_desc); /* * Returns with the root vnode locked. */ void rootfs_init() { struct thread *td; struct vnode *vp; int error; td = curthread; error = rootfs_allocv(rootfs_nodes, &vp, td); vp->v_type = VDIR; vp->v_flag |= VROOT; rootvnode = vp; } /* * Release our vnodes. */ void rootfs_fini() { struct rootfs_node *de; struct vnode *vp; de = rootfs_nodes; while (de->de_type != ROOTFS_NONE) { if (de->de_vnode != NULL) { vp = de->de_vnode; vrele(vp); } de++; } } static int rootfs_allocv(de, vpp, td) struct rootfs_node *de; struct vnode **vpp; struct thread *td; { struct vnode *vp; int i; i = 0; loop: vp = de->de_vnode; if (vp != NULL) { if (vget(vp, LK_EXCLUSIVE, td)) goto loop; *vpp = vp; return (0); } getnewvnode(VT_NON, NULL, rootfs_vnodeop_p, &vp); vp->v_type = VDIR; vp->v_data = (void *)de; de->de_vnode = vp; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); *vpp = vp; return (0); }