static int devfs_vop_nsymlink(struct vop_nsymlink_args *ap) { struct devfs_node *dnode = DEVFS_NODE(ap->a_dvp); struct devfs_node *node; size_t targetlen; if (!devfs_node_is_accessible(dnode)) return ENOENT; ap->a_vap->va_type = VLNK; if ((dnode->node_type != Nroot) && (dnode->node_type != Ndir)) goto out; lockmgr(&devfs_lock, LK_EXCLUSIVE); devfs_allocvp(ap->a_dvp->v_mount, ap->a_vpp, Nlink, ap->a_nch->ncp->nc_name, dnode, NULL); targetlen = strlen(ap->a_target); if (*ap->a_vpp) { node = DEVFS_NODE(*ap->a_vpp); node->flags |= DEVFS_USER_CREATED; node->symlink_namelen = targetlen; node->symlink_name = kmalloc(targetlen + 1, M_DEVFS, M_WAITOK); memcpy(node->symlink_name, ap->a_target, targetlen); node->symlink_name[targetlen] = '\0'; cache_setunresolved(ap->a_nch); cache_setvp(ap->a_nch, *ap->a_vpp); } lockmgr(&devfs_lock, LK_RELEASE); out: return ((*ap->a_vpp == NULL) ? ENOTDIR : 0); }
static int devfs_vop_nmkdir(struct vop_nmkdir_args *ap) { struct devfs_node *dnode = DEVFS_NODE(ap->a_dvp); struct devfs_node *node; if (!devfs_node_is_accessible(dnode)) return ENOENT; if ((dnode->node_type != Nroot) && (dnode->node_type != Ndir)) goto out; lockmgr(&devfs_lock, LK_EXCLUSIVE); devfs_allocvp(ap->a_dvp->v_mount, ap->a_vpp, Ndir, ap->a_nch->ncp->nc_name, dnode, NULL); if (*ap->a_vpp) { node = DEVFS_NODE(*ap->a_vpp); node->flags |= DEVFS_USER_CREATED; cache_setunresolved(ap->a_nch); cache_setvp(ap->a_nch, *ap->a_vpp); } lockmgr(&devfs_lock, LK_RELEASE); out: return ((*ap->a_vpp == NULL) ? ENOTDIR : 0); }
static int puffs_vnop_mknod(struct vop_nmknod_args *ap) { PUFFS_MSG_VARS(vn, mknod); struct vnode *dvp = ap->a_dvp; struct vattr *vap = ap->a_vap; struct puffs_node *dpn = VPTOPP(dvp); struct nchandle *nch = ap->a_nch; struct namecache *ncp = nch->ncp; struct ucred *cred = ap->a_cred; struct mount *mp = dvp->v_mount; struct puffs_mount *pmp = MPTOPUFFSMP(mp); int error; if (!EXISTSOP(pmp, MKNOD)) return EOPNOTSUPP; DPRINTF(("puffs_mknod: dvp %p, name: %s\n", dvp, ncp->nc_name)); if (vap->va_type != VFIFO) return EINVAL; if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) { DPRINTF(("puffs_vnop_mknod: EAGAIN on ncp %p %s\n", ncp, ncp->nc_name)); return EAGAIN; } PUFFS_MSG_ALLOC(vn, mknod); puffs_makecn(&mknod_msg->pvnr_cn, &mknod_msg->pvnr_cn_cred, ncp, cred); mknod_msg->pvnr_va = *ap->a_vap; puffs_msg_setinfo(park_mknod, PUFFSOP_VN, PUFFS_VN_MKNOD, VPTOPNC(dvp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_mknod, dvp->v_data, NULL, error); error = checkerr(pmp, error, __func__); if (error) goto out; error = puffs_newnode(mp, dvp, ap->a_vpp, mknod_msg->pvnr_newnode, vap->va_type); if (error) puffs_abortbutton(pmp, PUFFS_ABORT_MKNOD, dpn->pn_cookie, mknod_msg->pvnr_newnode, ncp, cred); out: vput(dvp); if (!error) { cache_setunresolved(nch); cache_setvp(nch, *ap->a_vpp); } PUFFS_MSG_RELEASE(mknod); return error; }
static int puffs_vnop_link(struct vop_nlink_args *ap) { PUFFS_MSG_VARS(vn, link); struct vnode *dvp = ap->a_dvp; struct vnode *vp = ap->a_vp; struct puffs_node *dpn = VPTOPP(dvp); struct puffs_node *pn = VPTOPP(vp); struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount); struct nchandle *nch = ap->a_nch; struct namecache *ncp = nch->ncp; struct ucred *cred = ap->a_cred; int error; if (!EXISTSOP(pmp, LINK)) return EOPNOTSUPP; if (vp->v_mount != dvp->v_mount) return EXDEV; if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) { DPRINTF(("puffs_vnop_link: EAGAIN on ncp %p %s\n", ncp, ncp->nc_name)); return EAGAIN; } PUFFS_MSG_ALLOC(vn, link); link_msg->pvnr_cookie_targ = VPTOPNC(vp); puffs_makecn(&link_msg->pvnr_cn, &link_msg->pvnr_cn_cred, ncp, cred); puffs_msg_setinfo(park_link, PUFFSOP_VN, PUFFS_VN_LINK, VPTOPNC(dvp)); puffs_msg_enqueue(pmp, park_link); error = puffs_msg_wait2(pmp, park_link, dpn, pn); PUFFS_MSG_RELEASE(link); error = checkerr(pmp, error, __func__); /* * XXX: stay in touch with the cache. I don't like this, but * don't have a better solution either. See also puffs_rename(). */ if (error == 0) { puffs_updatenode(pn, PUFFS_UPDATECTIME); } vput(dvp); if (error == 0) { cache_setunresolved(nch); cache_setvp(nch, vp); } return error; }
static int puffs_vnop_symlink(struct vop_nsymlink_args *ap) { PUFFS_MSG_VARS(vn, symlink); struct vnode *dvp = ap->a_dvp; struct puffs_node *dpn = VPTOPP(dvp); struct mount *mp = dvp->v_mount; struct puffs_mount *pmp = MPTOPUFFSMP(dvp->v_mount); struct nchandle *nch = ap->a_nch; struct namecache *ncp = nch->ncp; struct ucred *cred = ap->a_cred; int error; if (!EXISTSOP(pmp, SYMLINK)) return EOPNOTSUPP; if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) { DPRINTF(("puffs_vnop_symlink: EAGAIN on ncp %p %s\n", ncp, ncp->nc_name)); return EAGAIN; } *ap->a_vpp = NULL; PUFFS_MSG_ALLOC(vn, symlink); puffs_makecn(&symlink_msg->pvnr_cn, &symlink_msg->pvnr_cn_cred, ncp, cred); symlink_msg->pvnr_va = *ap->a_vap; (void)strlcpy(symlink_msg->pvnr_link, ap->a_target, sizeof(symlink_msg->pvnr_link)); puffs_msg_setinfo(park_symlink, PUFFSOP_VN, PUFFS_VN_SYMLINK, VPTOPNC(dvp)); PUFFS_MSG_ENQUEUEWAIT2(pmp, park_symlink, dvp->v_data, NULL, error); error = checkerr(pmp, error, __func__); if (error) goto out; error = puffs_newnode(mp, dvp, ap->a_vpp, symlink_msg->pvnr_newnode, VLNK); if (error) puffs_abortbutton(pmp, PUFFS_ABORT_SYMLINK, dpn->pn_cookie, symlink_msg->pvnr_newnode, ncp, cred); out: vput(dvp); PUFFS_MSG_RELEASE(symlink); if (!error) { cache_setunresolved(nch); cache_setvp(nch, *ap->a_vpp); } return error; }
static int tmpfs_nsymlink(struct vop_nsymlink_args *v) { struct vnode *dvp = v->a_dvp; struct vnode **vpp = v->a_vpp; struct namecache *ncp = v->a_nch->ncp; struct vattr *vap = v->a_vap; struct ucred *cred = v->a_cred; char *target = v->a_target; int error; vap->va_type = VLNK; error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, target); if (error == 0) { tmpfs_knote(*vpp, NOTE_WRITE); cache_setunresolved(v->a_nch); cache_setvp(v->a_nch, *vpp); } return error; }
static int tmpfs_ncreate(struct vop_ncreate_args *ap) { struct vnode *dvp = ap->a_dvp; struct vnode **vpp = ap->a_vpp; struct namecache *ncp = ap->a_nch->ncp; struct vattr *vap = ap->a_vap; struct ucred *cred = ap->a_cred; int error; KKASSERT(vap->va_type == VREG || vap->va_type == VSOCK); error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, NULL); if (error == 0) { cache_setunresolved(ap->a_nch); cache_setvp(ap->a_nch, *vpp); tmpfs_knote(dvp, NOTE_WRITE); } return (error); }
static int tmpfs_nmkdir(struct vop_nmkdir_args *v) { struct vnode *dvp = v->a_dvp; struct vnode **vpp = v->a_vpp; struct namecache *ncp = v->a_nch->ncp; struct vattr *vap = v->a_vap; struct ucred *cred = v->a_cred; int error; KKASSERT(vap->va_type == VDIR); error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, NULL); if (error == 0) { cache_setunresolved(v->a_nch); cache_setvp(v->a_nch, *vpp); tmpfs_knote(dvp, NOTE_WRITE | NOTE_LINK); } return error; }
static int tmpfs_nmknod(struct vop_nmknod_args *v) { struct vnode *dvp = v->a_dvp; struct vnode **vpp = v->a_vpp; struct namecache *ncp = v->a_nch->ncp; struct vattr *vap = v->a_vap; struct ucred *cred = v->a_cred; int error; if (vap->va_type != VBLK && vap->va_type != VCHR && vap->va_type != VFIFO) { return (EINVAL); } error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, NULL); if (error == 0) { cache_setunresolved(v->a_nch); cache_setvp(v->a_nch, *vpp); tmpfs_knote(dvp, NOTE_WRITE); } return error; }
static int tmpfs_nlink(struct vop_nlink_args *v) { struct vnode *dvp = v->a_dvp; struct vnode *vp = v->a_vp; struct namecache *ncp = v->a_nch->ncp; struct tmpfs_dirent *de; struct tmpfs_node *node; struct tmpfs_node *dnode; struct mount *mp; int error; mp = dvp->v_mount; KKASSERT(dvp != vp); /* XXX When can this be false? */ node = VP_TO_TMPFS_NODE(vp); dnode = VP_TO_TMPFS_NODE(dvp); TMPFS_NODE_LOCK(dnode); /* XXX: Why aren't the following two tests done by the caller? */ /* Hard links of directories are forbidden. */ if (vp->v_type == VDIR) { error = EPERM; goto out; } /* Cannot create cross-device links. */ if (dvp->v_mount != vp->v_mount) { error = EXDEV; goto out; } /* Ensure that we do not overflow the maximum number of links imposed * by the system. */ KKASSERT(node->tn_links <= LINK_MAX); if (node->tn_links >= LINK_MAX) { error = EMLINK; goto out; } /* We cannot create links of files marked immutable or append-only. */ if (node->tn_flags & (IMMUTABLE | APPEND)) { error = EPERM; goto out; } /* Allocate a new directory entry to represent the node. */ error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node, ncp->nc_name, ncp->nc_nlen, &de); if (error != 0) goto out; /* Insert the new directory entry into the appropriate directory. */ tmpfs_dir_attach(dnode, de); /* vp link count has changed, so update node times. */ TMPFS_NODE_LOCK(node); node->tn_status |= TMPFS_NODE_CHANGED; TMPFS_NODE_UNLOCK(node); tmpfs_update(vp); tmpfs_knote(vp, NOTE_LINK); cache_setunresolved(v->a_nch); cache_setvp(v->a_nch, vp); error = 0; out: TMPFS_NODE_UNLOCK(dnode); if (error == 0) tmpfs_knote(dvp, NOTE_WRITE); return error; }
/* * vop_compat_nremove { struct nchandle *a_nch, XXX STOPGAP FUNCTION * struct vnode *a_dvp, * struct ucred *a_cred } */ int vop_compat_nremove(struct vop_nremove_args *ap) { struct thread *td = curthread; struct componentname cnp; struct nchandle *nch; struct namecache *ncp; struct vnode *dvp; struct vnode *vp; int error; /* * Sanity checks, get a locked directory vnode. */ nch = ap->a_nch; /* locked namecache node */ ncp = nch->ncp; dvp = ap->a_dvp; if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) { kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n", ncp, ncp->nc_name); return(EAGAIN); } /* * Setup the cnp for a traditional vop_old_lookup() call. The lookup * caches all information required to delete the entry in the * directory inode. We expect a return code of 0 for the DELETE * case (meaning that a vp has been found). The cnp must simulated * a saved-name situation. */ bzero(&cnp, sizeof(cnp)); cnp.cn_nameiop = NAMEI_DELETE; cnp.cn_flags = CNP_LOCKPARENT; cnp.cn_nameptr = ncp->nc_name; cnp.cn_namelen = ncp->nc_nlen; cnp.cn_cred = ap->a_cred; cnp.cn_td = td; /* * The vnode must be a directory and must not represent the * current directory. */ vp = NULL; error = vop_old_lookup(ap->a_head.a_ops, dvp, &vp, &cnp); if (error == 0 && vp->v_type == VDIR) error = EPERM; if (error == 0) { KKASSERT((cnp.cn_flags & CNP_PDIRUNLOCK) == 0); error = VOP_OLD_REMOVE(dvp, vp, &cnp); if (error == 0) { cache_setunresolved(nch); cache_setvp(nch, NULL); cache_inval_vp(vp, CINV_DESTROY); } } if (vp) { if (dvp == vp) vrele(vp); else vput(vp); } if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0) vn_unlock(dvp); vrele(dvp); return (error); }
/* * vop_compat_nwhiteout { struct nchandle *a_nch, XXX STOPGAP FUNCTION * struct vnode *a_dvp, * struct ucred *a_cred, * int a_flags } * * Issie a whiteout operation (create, lookup, or delete). Compatibility * requires us to issue the appropriate VOP_OLD_LOOKUP before we issue * VOP_OLD_WHITEOUT in order to setup the directory inode's i_offset and i_count * (e.g. in UFS) for the NAMEI_CREATE and NAMEI_DELETE ops. For NAMEI_LOOKUP * no lookup is necessary. */ int vop_compat_nwhiteout(struct vop_nwhiteout_args *ap) { struct thread *td = curthread; struct componentname cnp; struct nchandle *nch; struct namecache *ncp; struct vnode *dvp; struct vnode *vp; int error; /* * Sanity checks, get a locked directory vnode. */ nch = ap->a_nch; /* locked namecache node */ ncp = nch->ncp; dvp = ap->a_dvp; if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) { kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n", ncp, ncp->nc_name); return(EAGAIN); } /* * Setup the cnp for a traditional vop_old_lookup() call. The lookup * caches all information required to create the entry in the * directory inode. We expect a return code of EJUSTRETURN for * the CREATE case. The cnp must simulated a saved-name situation. */ bzero(&cnp, sizeof(cnp)); cnp.cn_nameiop = ap->a_flags; cnp.cn_flags = CNP_LOCKPARENT; cnp.cn_nameptr = ncp->nc_name; cnp.cn_namelen = ncp->nc_nlen; cnp.cn_cred = ap->a_cred; cnp.cn_td = td; vp = NULL; /* * EJUSTRETURN should be returned for the CREATE or DELETE cases. * The VFS has setup the directory inode for the create. The dvp we * passed in is expected to remain in a locked state. * * If the VOP_OLD_WHITEOUT is successful we are responsible for updating * the cache state of the locked ncp that was passed to us. */ switch(ap->a_flags) { case NAMEI_DELETE: cnp.cn_flags |= CNP_DOWHITEOUT; /* fall through */ case NAMEI_CREATE: error = vop_old_lookup(ap->a_head.a_ops, dvp, &vp, &cnp); if (error == EJUSTRETURN) { KKASSERT((cnp.cn_flags & CNP_PDIRUNLOCK) == 0); error = VOP_OLD_WHITEOUT(dvp, &cnp, ap->a_flags); if (error == 0) cache_setunresolved(nch); } else { if (error == 0) { vput(vp); vp = NULL; error = EEXIST; } KKASSERT(vp == NULL); } break; case NAMEI_LOOKUP: error = VOP_OLD_WHITEOUT(dvp, NULL, ap->a_flags); break; default: error = EINVAL; break; } if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0) vn_unlock(dvp); vrele(dvp); return (error); }
/* * vop_compat_nlink { struct nchandle *a_nch, XXX STOPGAP FUNCTION * struct vnode *a_dvp, * struct vnode *a_vp, * struct ucred *a_cred } * * The passed vp is locked and represents the source. The passed ncp is * locked and represents the target to create. */ int vop_compat_nlink(struct vop_nlink_args *ap) { struct thread *td = curthread; struct componentname cnp; struct nchandle *nch; struct namecache *ncp; struct vnode *dvp; struct vnode *tvp; int error; /* * Sanity checks, get a locked directory vnode. */ nch = ap->a_nch; /* locked namecache node */ ncp = nch->ncp; dvp = ap->a_dvp; if ((error = vget(dvp, LK_EXCLUSIVE)) != 0) { kprintf("[diagnostic] vop_compat_resolve: EAGAIN on ncp %p %s\n", ncp, ncp->nc_name); return(EAGAIN); } /* * Setup the cnp for a traditional vop_old_lookup() call. The lookup * caches all information required to create the entry in the * directory inode. We expect a return code of EJUSTRETURN for * the CREATE case. The cnp must simulated a saved-name situation. */ bzero(&cnp, sizeof(cnp)); cnp.cn_nameiop = NAMEI_CREATE; cnp.cn_flags = CNP_LOCKPARENT; cnp.cn_nameptr = ncp->nc_name; cnp.cn_namelen = ncp->nc_nlen; cnp.cn_cred = ap->a_cred; cnp.cn_td = td; tvp = NULL; error = vop_old_lookup(ap->a_head.a_ops, dvp, &tvp, &cnp); /* * EJUSTRETURN should be returned for this case, which means that * the VFS has setup the directory inode for the create. The dvp we * passed in is expected to remain in a locked state. * * If the VOP_OLD_LINK is successful we are responsible for updating * the cache state of the locked ncp that was passed to us. */ if (error == EJUSTRETURN) { KKASSERT((cnp.cn_flags & CNP_PDIRUNLOCK) == 0); error = VOP_OLD_LINK(dvp, ap->a_vp, &cnp); if (error == 0) { cache_setunresolved(nch); cache_setvp(nch, ap->a_vp); } } else { if (error == 0) { vput(tvp); error = EEXIST; } } if ((cnp.cn_flags & CNP_PDIRUNLOCK) == 0) vn_unlock(dvp); vrele(dvp); return (error); }
/* * Common code for vnode open operations. Check permissions, and call * the VOP_NOPEN or VOP_NCREATE routine. * * The caller is responsible for setting up nd with nlookup_init() and * for cleaning it up with nlookup_done(), whether we return an error * or not. * * On success nd->nl_open_vp will hold a referenced and, if requested, * locked vnode. A locked vnode is requested via NLC_LOCKVP. If fp * is non-NULL the vnode will be installed in the file pointer. * * NOTE: The vnode is referenced just once on return whether or not it * is also installed in the file pointer. */ int vn_open(struct nlookupdata *nd, struct file *fp, int fmode, int cmode) { struct vnode *vp; struct ucred *cred = nd->nl_cred; struct vattr vat; struct vattr *vap = &vat; int error; u_int flags; uint64_t osize; struct mount *mp; /* * Certain combinations are illegal */ if ((fmode & (FWRITE | O_TRUNC)) == O_TRUNC) return(EACCES); /* * Lookup the path and create or obtain the vnode. After a * successful lookup a locked nd->nl_nch will be returned. * * The result of this section should be a locked vnode. * * XXX with only a little work we should be able to avoid locking * the vnode if FWRITE, O_CREAT, and O_TRUNC are *not* set. */ nd->nl_flags |= NLC_OPEN; if (fmode & O_APPEND) nd->nl_flags |= NLC_APPEND; if (fmode & O_TRUNC) nd->nl_flags |= NLC_TRUNCATE; if (fmode & FREAD) nd->nl_flags |= NLC_READ; if (fmode & FWRITE) nd->nl_flags |= NLC_WRITE; if ((fmode & O_EXCL) == 0 && (fmode & O_NOFOLLOW) == 0) nd->nl_flags |= NLC_FOLLOW; if (fmode & O_CREAT) { /* * CONDITIONAL CREATE FILE CASE * * Setting NLC_CREATE causes a negative hit to store * the negative hit ncp and not return an error. Then * nc_error or nc_vp may be checked to see if the ncp * represents a negative hit. NLC_CREATE also requires * write permission on the governing directory or EPERM * is returned. */ nd->nl_flags |= NLC_CREATE; nd->nl_flags |= NLC_REFDVP; bwillinode(1); error = nlookup(nd); } else { /* * NORMAL OPEN FILE CASE */ error = nlookup(nd); } if (error) return (error); /* * split case to allow us to re-resolve and retry the ncp in case * we get ESTALE. */ again: if (fmode & O_CREAT) { if (nd->nl_nch.ncp->nc_vp == NULL) { if ((error = ncp_writechk(&nd->nl_nch)) != 0) return (error); VATTR_NULL(vap); vap->va_type = VREG; vap->va_mode = cmode; if (fmode & O_EXCL) vap->va_vaflags |= VA_EXCLUSIVE; error = VOP_NCREATE(&nd->nl_nch, nd->nl_dvp, &vp, nd->nl_cred, vap); if (error) return (error); fmode &= ~O_TRUNC; /* locked vnode is returned */ } else { if (fmode & O_EXCL) { error = EEXIST; } else { error = cache_vget(&nd->nl_nch, cred, LK_EXCLUSIVE, &vp); } if (error) return (error); fmode &= ~O_CREAT; } } else { error = cache_vget(&nd->nl_nch, cred, LK_EXCLUSIVE, &vp); if (error) return (error); } /* * We have a locked vnode and ncp now. Note that the ncp will * be cleaned up by the caller if nd->nl_nch is left intact. */ if (vp->v_type == VLNK) { error = EMLINK; goto bad; } if (vp->v_type == VSOCK) { error = EOPNOTSUPP; goto bad; } if ((fmode & O_CREAT) == 0) { if (fmode & (FWRITE | O_TRUNC)) { if (vp->v_type == VDIR) { error = EISDIR; goto bad; } error = vn_writechk(vp, &nd->nl_nch); if (error) { /* * Special stale handling, re-resolve the * vnode. */ if (error == ESTALE) { vput(vp); vp = NULL; cache_setunresolved(&nd->nl_nch); error = cache_resolve(&nd->nl_nch, cred); if (error == 0) goto again; } goto bad; } } } if (fmode & O_TRUNC) { vn_unlock(vp); /* XXX */ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); /* XXX */ osize = vp->v_filesize; VATTR_NULL(vap); vap->va_size = 0; error = VOP_SETATTR(vp, vap, cred); if (error) goto bad; error = VOP_GETATTR(vp, vap); if (error) goto bad; mp = vq_vptomp(vp); VFS_ACCOUNT(mp, vap->va_uid, vap->va_gid, -osize); } /* * Set or clear VNSWAPCACHE on the vp based on nd->nl_nch.ncp->nc_flag. * These particular bits a tracked all the way from the root. * * NOTE: Might not work properly on NFS servers due to the * disconnected namecache. */ flags = nd->nl_nch.ncp->nc_flag; if ((flags & (NCF_UF_CACHE | NCF_UF_PCACHE)) && (flags & (NCF_SF_NOCACHE | NCF_SF_PNOCACHE)) == 0) { vsetflags(vp, VSWAPCACHE); } else { vclrflags(vp, VSWAPCACHE); } /* * Setup the fp so VOP_OPEN can override it. No descriptor has been * associated with the fp yet so we own it clean. * * f_nchandle inherits nl_nch. This used to be necessary only for * directories but now we do it unconditionally so f*() ops * such as fchmod() can access the actual namespace that was * used to open the file. */ if (fp) { if (nd->nl_flags & NLC_APPENDONLY) fmode |= FAPPENDONLY; fp->f_nchandle = nd->nl_nch; cache_zero(&nd->nl_nch); cache_unlock(&fp->f_nchandle); } /* * Get rid of nl_nch. vn_open does not return it (it returns the * vnode or the file pointer). Note: we can't leave nl_nch locked * through the VOP_OPEN anyway since the VOP_OPEN may block, e.g. * on /dev/ttyd0 */ if (nd->nl_nch.ncp) cache_put(&nd->nl_nch); error = VOP_OPEN(vp, fmode, cred, fp); if (error) { /* * setting f_ops to &badfileops will prevent the descriptor * code from trying to close and release the vnode, since * the open failed we do not want to call close. */ if (fp) { fp->f_data = NULL; fp->f_ops = &badfileops; } goto bad; } #if 0 /* * Assert that VREG files have been setup for vmio. */ KASSERT(vp->v_type != VREG || vp->v_object != NULL, ("vn_open: regular file was not VMIO enabled!")); #endif /* * Return the vnode. XXX needs some cleaning up. The vnode is * only returned in the fp == NULL case. */ if (fp == NULL) { nd->nl_open_vp = vp; nd->nl_vp_fmode = fmode; if ((nd->nl_flags & NLC_LOCKVP) == 0) vn_unlock(vp); } else { vput(vp); } return (0); bad: if (vp) vput(vp); return (error); }
static int tmpfs_nremove(struct vop_nremove_args *v) { struct vnode *dvp = v->a_dvp; struct namecache *ncp = v->a_nch->ncp; struct vnode *vp; int error; struct tmpfs_dirent *de; struct tmpfs_mount *tmp; struct tmpfs_node *dnode; struct tmpfs_node *node; /* * We have to acquire the vp from v->a_nch because we will likely * unresolve the namecache entry, and a vrele/vput is needed to * trigger the tmpfs_inactive/tmpfs_reclaim sequence. * * We have to use vget to clear any inactive state on the vnode, * otherwise the vnode may remain inactive and thus tmpfs_inactive * will not get called when we release it. */ error = cache_vget(v->a_nch, v->a_cred, LK_SHARED, &vp); KKASSERT(error == 0); vn_unlock(vp); if (vp->v_type == VDIR) { error = EISDIR; goto out; } dnode = VP_TO_TMPFS_DIR(dvp); node = VP_TO_TMPFS_NODE(vp); tmp = VFS_TO_TMPFS(vp->v_mount); de = tmpfs_dir_lookup(dnode, node, ncp); if (de == NULL) { error = ENOENT; goto out; } /* Files marked as immutable or append-only cannot be deleted. */ if ((node->tn_flags & (IMMUTABLE | APPEND | NOUNLINK)) || (dnode->tn_flags & APPEND)) { error = EPERM; goto out; } /* Remove the entry from the directory; as it is a file, we do not * have to change the number of hard links of the directory. */ tmpfs_dir_detach(dnode, de); /* Free the directory entry we just deleted. Note that the node * referred by it will not be removed until the vnode is really * reclaimed. */ tmpfs_free_dirent(tmp, de); if (node->tn_links > 0) { TMPFS_NODE_LOCK(node); node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ TMPFS_NODE_MODIFIED; TMPFS_NODE_UNLOCK(node); } cache_setunresolved(v->a_nch); cache_setvp(v->a_nch, NULL); tmpfs_knote(vp, NOTE_DELETE); /*cache_inval_vp(vp, CINV_DESTROY);*/ tmpfs_knote(dvp, NOTE_WRITE); error = 0; out: vrele(vp); return error; }
static int tmpfs_nrmdir(struct vop_nrmdir_args *v) { struct vnode *dvp = v->a_dvp; struct namecache *ncp = v->a_nch->ncp; struct vnode *vp; struct tmpfs_dirent *de; struct tmpfs_mount *tmp; struct tmpfs_node *dnode; struct tmpfs_node *node; int error; /* * We have to acquire the vp from v->a_nch because we will likely * unresolve the namecache entry, and a vrele/vput is needed to * trigger the tmpfs_inactive/tmpfs_reclaim sequence. * * We have to use vget to clear any inactive state on the vnode, * otherwise the vnode may remain inactive and thus tmpfs_inactive * will not get called when we release it. */ error = cache_vget(v->a_nch, v->a_cred, LK_SHARED, &vp); KKASSERT(error == 0); vn_unlock(vp); /* * Prevalidate so we don't hit an assertion later */ if (vp->v_type != VDIR) { error = ENOTDIR; goto out; } tmp = VFS_TO_TMPFS(dvp->v_mount); dnode = VP_TO_TMPFS_DIR(dvp); node = VP_TO_TMPFS_DIR(vp); /* Directories with more than two entries ('.' and '..') cannot be * removed. */ if (node->tn_size > 0) { error = ENOTEMPTY; goto out; } if ((dnode->tn_flags & APPEND) || (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) { error = EPERM; goto out; } /* This invariant holds only if we are not trying to remove "..". * We checked for that above so this is safe now. */ KKASSERT(node->tn_dir.tn_parent == dnode); /* Get the directory entry associated with node (vp). This was * filled by tmpfs_lookup while looking up the entry. */ de = tmpfs_dir_lookup(dnode, node, ncp); KKASSERT(TMPFS_DIRENT_MATCHES(de, ncp->nc_name, ncp->nc_nlen)); /* Check flags to see if we are allowed to remove the directory. */ if ((dnode->tn_flags & APPEND) || node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) { error = EPERM; goto out; } /* Detach the directory entry from the directory (dnode). */ tmpfs_dir_detach(dnode, de); /* No vnode should be allocated for this entry from this point */ TMPFS_NODE_LOCK(node); TMPFS_ASSERT_ELOCKED(node); TMPFS_NODE_LOCK(dnode); TMPFS_ASSERT_ELOCKED(dnode); #if 0 /* handled by tmpfs_free_node */ KKASSERT(node->tn_links > 0); node->tn_links--; node->tn_dir.tn_parent = NULL; #endif node->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED | \ TMPFS_NODE_MODIFIED; #if 0 /* handled by tmpfs_free_node */ KKASSERT(dnode->tn_links > 0); dnode->tn_links--; #endif dnode->tn_status |= TMPFS_NODE_ACCESSED | \ TMPFS_NODE_CHANGED | TMPFS_NODE_MODIFIED; TMPFS_NODE_UNLOCK(dnode); TMPFS_NODE_UNLOCK(node); /* Free the directory entry we just deleted. Note that the node * referred by it will not be removed until the vnode is really * reclaimed. */ tmpfs_free_dirent(tmp, de); /* Release the deleted vnode (will destroy the node, notify * interested parties and clean it from the cache). */ TMPFS_NODE_LOCK(dnode); dnode->tn_status |= TMPFS_NODE_CHANGED; TMPFS_NODE_UNLOCK(dnode); tmpfs_update(dvp); cache_setunresolved(v->a_nch); cache_setvp(v->a_nch, NULL); /*cache_inval_vp(vp, CINV_DESTROY);*/ tmpfs_knote(dvp, NOTE_WRITE | NOTE_LINK); error = 0; out: vrele(vp); return error; }