diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index cebd09b..f5a9703 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -596,7 +596,7 @@ retry: * close() were performed on it). */ if (delfp) { - (void)closef(delfp, td); + closef(delfp, p); if (holdleaders) { spin_lock_wr(&fdp->fd_spin); fdp->fd_holdleaderscount--; @@ -830,7 +830,7 @@ kern_close(int fd) knote_fdclose(p, fd); rel_mplock(); } - error = closef(fp, td); + error = closef(fp, p); if (holdleaders) { spin_lock_wr(&fdp->fd_spin); fdp->fd_holdleaderscount--; @@ -1180,6 +1180,98 @@ fdavail(struct proc *p, int n) } /* + * Revoke open descriptors referencing (f_data, f_type) + * + * Any revoke executed within a prison is only able to + * revoke descriptors for processes within that prison. + * + * Returns 0 on success or an error code. + */ +struct fdrevoke_info { + void *data; + short type; + short unused; + int count; + int again; + struct ucred *cred; + struct file *nfp; +}; + +static int fdrevoke_callback(struct proc *p, void *vinfo); + +int +fdrevoke(void *f_data, short f_type, struct ucred *cred) +{ + struct fdrevoke_info info; + int error; + + bzero(&info, sizeof(info)); + info.data = f_data; + info.type = f_type; + info.cred = cred; + error = falloc(NULL, &info.nfp, NULL); + if (error) + return (error); + do { + info.again = 0; + allproc_scan(fdrevoke_callback, &info); + } while (info.again != 0); + fdrop(info.nfp); + return(info.count); +} + +static int +fdrevoke_callback(struct proc *p, void *vinfo) +{ + struct fdrevoke_info *info = vinfo; + struct filedesc *fdp; + struct file *fp; + int n; + + if (p->p_stat == SIDL || p->p_stat == SZOMB) + return(0); + if (info->cred->cr_prison && + info->cred->cr_prison != p->p_ucred->cr_prison) { + return(0); + } + + /* + * If the controlling terminal of the process matches the + * vnode being revoked we clear the controlling terminal. + * + * The normal spec_close() may not catch this because it + * uses curproc instead of p. + */ + if (p->p_session && info->type == DTYPE_VNODE && + info->data == p->p_session->s_ttyvp) { + p->p_session->s_ttyvp = NULL; + vrele(info->data); + } + + /* + * Locate and close any matching file descriptors. + */ + if ((fdp = p->p_fd) == NULL) + return(0); + spin_lock_wr(&fdp->fd_spin); + for (n = 0; n < fdp->fd_nfiles; ++n) { + if ((fp = fdp->fd_files[n].fp) == NULL) + continue; + if (info->data == fp->f_data && info->type == fp->f_type) { + fhold(info->nfp); + fdp->fd_files[n].fp = info->nfp; + spin_unlock_wr(&fdp->fd_spin); + closef(fp, p); + spin_lock_wr(&fdp->fd_spin); + ++info->count; + info->again = 1; + } + } + spin_unlock_wr(&fdp->fd_spin); + return(0); +} + +/* * falloc: * Create a new open file structure and reserve a file decriptor * for the process that refers to it. @@ -1630,8 +1722,6 @@ again: void fdfree(struct proc *p) { - /* Take any thread of p */ - struct thread *td = FIRST_LWP_IN_PROC(p)->lwp_thread; struct filedesc *fdp = p->p_fd; struct fdnode *fdnode; int i; @@ -1731,7 +1821,7 @@ fdfree(struct proc *p) */ for (i = 0; i <= fdp->fd_lastfile; ++i) { if (fdp->fd_files[i].fp) - closef(fdp->fd_files[i].fp, td); + closef(fdp->fd_files[i].fp, p); } if (fdp->fd_files != fdp->fd_builtin_files) kfree(fdp->fd_files, M_FILEDESC); @@ -1879,8 +1969,6 @@ is_unsafe(struct file *fp) void setugidsafety(struct proc *p) { - /* Take any thread of p */ - struct thread *td = FIRST_LWP_IN_PROC(p)->lwp_thread; struct filedesc *fdp = p->p_fd; int i; @@ -1905,7 +1993,7 @@ setugidsafety(struct proc *p) * a race while close blocks. */ if ((fp = funsetfd_locked(fdp, i)) != NULL) - closef(fp, td); + closef(fp, p); } } } @@ -1918,8 +2006,6 @@ setugidsafety(struct proc *p) void fdcloseexec(struct proc *p) { - /* Take any thread of p */ - struct thread *td = FIRST_LWP_IN_PROC(p)->lwp_thread; struct filedesc *fdp = p->p_fd; int i; @@ -1943,7 +2029,7 @@ fdcloseexec(struct proc *p) * a race while close blocks. */ if ((fp = funsetfd_locked(fdp, i)) != NULL) - closef(fp, td); + closef(fp, p); } } } @@ -2010,21 +2096,15 @@ fdcheckstd(struct proc *p) * MPALMOSTSAFE - acquires mplock for VOP operations */ int -closef(struct file *fp, struct thread *td) +closef(struct file *fp, struct proc *p) { struct vnode *vp; struct flock lf; struct filedesc_to_leader *fdtol; - struct proc *p; if (fp == NULL) return (0); - if (td == NULL) { - td = curthread; - p = NULL; /* allow no proc association */ - } else { - p = td->td_proc; /* can also be NULL */ - } + /* * POSIX record locking dictates that any close releases ALL * locks owned by this process. This is handled by setting @@ -2323,9 +2403,7 @@ allfiles_scan_exclusive(int (*callback)(struct file *, void *), void *data) spin_lock_wr(&filehead_spin); LIST_FOREACH(fp, &filehead, f_list) { - spin_lock_wr(&fp->f_spin); res = callback(fp, data); - spin_unlock_wr(&fp->f_spin); if (res < 0) break; } diff --git a/sys/kern/tty.c b/sys/kern/tty.c index c252227..4f14e1b 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -342,7 +342,7 @@ retry: VOP_CLOSE(vp, FREAD|FWRITE); } if (dorevoke) - VOP_REVOKE(vp, REVOKEALL); + vrevoke(vp, proc0.p_ucred); vn_unlock(vp); vdrop(vp); } else { diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c index c12e78a..f30f340 100644 --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -1265,7 +1265,7 @@ unp_gc_checkrefs(struct file *fp, void *data) static int unp_gc_clearmarks(struct file *fp, void *data __unused) { - fp->f_flag &= ~(FMARK|FDEFER); + atomic_clear_int(&fp->f_flag, FMARK | FDEFER); return(0); } @@ -1289,7 +1289,7 @@ unp_gc_checkmarks(struct file *fp, void *data) * and un-mark it */ if (fp->f_flag & FDEFER) { - fp->f_flag &= ~FDEFER; + atomic_clear_int(&fp->f_flag, FDEFER); --info->defer; } else { /* @@ -1309,7 +1309,7 @@ unp_gc_checkmarks(struct file *fp, void *data) * If it got this far then it must be * externally accessible. */ - fp->f_flag |= FMARK; + atomic_set_int(&fp->f_flag, FMARK); } /* * either it was defered, or it is externally @@ -1323,7 +1323,6 @@ unp_gc_checkmarks(struct file *fp, void *data) !(so->so_proto->pr_flags & PR_RIGHTS)) return(0); #ifdef notdef - XXX note: exclusive fp->f_spin lock held if (so->so_rcv.sb_flags & SB_LOCK) { /* * This is problematical; it's not clear @@ -1406,14 +1405,10 @@ unp_mark(struct file *fp, void *data) { struct unp_gc_info *info = data; - if (info->locked_fp != fp) - spin_lock_wr(&fp->f_spin); if ((fp->f_flag & FMARK) == 0) { ++info->defer; - fp->f_flag |= (FMARK|FDEFER); + atomic_set_int(&fp->f_flag, FMARK | FDEFER); } - if (info->locked_fp != fp) - spin_unlock_wr(&fp->f_spin); } static void diff --git a/sys/kern/vfs_default.c b/sys/kern/vfs_default.c index fe3c033..70ccdb5 100644 --- a/sys/kern/vfs_default.c +++ b/sys/kern/vfs_default.c @@ -88,7 +88,6 @@ struct vop_ops default_vnode_vops = { .vop_poll = vop_nopoll, .vop_readlink = (void *)vop_einval, .vop_reallocblks = (void *)vop_eopnotsupp, - .vop_revoke = vop_stdrevoke, .vop_strategy = vop_nostrategy, .vop_getacl = (void *)vop_eopnotsupp, .vop_setacl = (void *)vop_eopnotsupp, diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index 9a4c039..d86e2e4 100644 --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -1205,21 +1206,12 @@ vclean_vxlocked(struct vnode *vp, int flags) * revoke { struct vnode *a_vp, int a_flags } */ int -vop_stdrevoke(struct vop_revoke_args *ap) +vrevoke(struct vnode *vp, struct ucred *cred) { - struct vnode *vp, *vq; + struct vnode *vq; lwkt_tokref ilock; cdev_t dev; - - KASSERT((ap->a_flags & REVOKEALL) != 0, ("vop_revoke")); - - vp = ap->a_vp; - - /* - * If the vnode is already dead don't try to revoke it - */ - if (vp->v_flag & VRECLAIMED) - return (0); + int error; /* * If the vnode has a device association, scrap all vnodes associated @@ -1229,8 +1221,10 @@ vop_stdrevoke(struct vop_revoke_args *ap) * The passed vp will probably show up in the list, do not VX lock * it twice! */ - if (vp->v_type != VCHR) - return(0); + if (vp->v_type != VCHR) { + error = fdrevoke(vp, DTYPE_VNODE, cred); + return (error); + } if ((dev = vp->v_rdev) == NULL) { if ((dev = get_dev(vp->v_umajor, vp->v_uminor)) == NULL) return(0); @@ -1238,12 +1232,10 @@ vop_stdrevoke(struct vop_revoke_args *ap) reference_dev(dev); lwkt_gettoken(&ilock, &spechash_token); while ((vq = SLIST_FIRST(&dev->si_hlist)) != NULL) { - if (vp != vq) - vx_get(vq); - if (vq == SLIST_FIRST(&dev->si_hlist)) - vgone_vxlocked(vq); - if (vp != vq) - vx_put(vq); + vref(vq); + fdrevoke(vq, DTYPE_VNODE, cred); + v_release_rdev(vq); + vrele(vq); } lwkt_reltoken(&ilock); release_dev(dev); diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index d174c9a..9c5a40b 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -3560,17 +3560,15 @@ sys_revoke(struct revoke_args *uap) cred = crhold(nd.nl_cred); nlookup_done(&nd); if (error == 0) { - if (vp->v_type != VCHR && vp->v_type != VBLK) - error = EINVAL; if (error == 0) error = VOP_GETATTR(vp, &vattr); if (error == 0 && cred->cr_uid != vattr.va_uid) error = priv_check_cred(cred, PRIV_ROOT, PRISON_ROOT); - if (error == 0 && count_udev(vp->v_umajor, vp->v_uminor) > 0) { - error = 0; - vx_lock(vp); - VOP_REVOKE(vp, REVOKEALL); - vx_unlock(vp); + if (error == 0 && (vp->v_type == VCHR || vp->v_type == VBLK)) { + if (count_udev(vp->v_umajor, vp->v_uminor) > 0) + error = vrevoke(vp, cred); + } else if (error == 0) { + error = vrevoke(vp, cred); } vrele(vp); } diff --git a/sys/kern/vfs_vopops.c b/sys/kern/vfs_vopops.c index 89ce089..ec92b3e 100644 --- a/sys/kern/vfs_vopops.c +++ b/sys/kern/vfs_vopops.c @@ -97,7 +97,6 @@ VNODEOP_DESC_INIT(write); VNODEOP_DESC_INIT(ioctl); VNODEOP_DESC_INIT(poll); VNODEOP_DESC_INIT(kqfilter); -VNODEOP_DESC_INIT(revoke); VNODEOP_DESC_INIT(mmap); VNODEOP_DESC_INIT(fsync); VNODEOP_DESC_INIT(old_remove); @@ -406,21 +405,6 @@ vop_kqfilter(struct vop_ops *ops, struct vnode *vp, struct knote *kn) } int -vop_revoke(struct vop_ops *ops, struct vnode *vp, int flags) -{ - struct vop_revoke_args ap; - int error; - - ap.a_head.a_desc = &vop_revoke_desc; - ap.a_head.a_ops = ops; - ap.a_vp = vp; - ap.a_flags = flags; - - DO_OPS(ops, error, &ap, vop_revoke); - return(error); -} - -int vop_mmap(struct vop_ops *ops, struct vnode *vp, int fflags, struct ucred *cred) { struct vop_mmap_args ap; @@ -1415,15 +1399,6 @@ vop_kqfilter_ap(struct vop_kqfilter_args *ap) } int -vop_revoke_ap(struct vop_revoke_args *ap) -{ - int error; - - DO_OPS(ap->a_head.a_ops, error, ap, vop_revoke); - return(error); -} - -int vop_mmap_ap(struct vop_mmap_args *ap) { int error; diff --git a/sys/sys/file.h b/sys/sys/file.h index 646fcc2..cd5e7dd 100644 --- a/sys/sys/file.h +++ b/sys/sys/file.h @@ -118,7 +118,7 @@ struct file { int f_count; /* reference count */ int f_msgcount; /* (U) reference count from message queue */ struct nchandle f_nchandle; /* namecache reference */ - struct spinlock f_spin; + struct spinlock f_spin; /* NOT USED */ }; #define DTYPE_VNODE 1 /* file */ diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h index d57e9d8..c3deef5 100644 --- a/sys/sys/filedesc.h +++ b/sys/sys/filedesc.h @@ -170,7 +170,8 @@ struct filedesc *fdinit (struct proc *p); struct filedesc *fdshare (struct proc *p); struct filedesc *fdcopy (struct proc *p); void fdfree (struct proc *p); -int closef (struct file *fp, struct thread *td); +int fdrevoke(void *f_data, short f_type, struct ucred *cred); +int closef (struct file *fp, struct proc *p); void fdcloseexec (struct proc *p); int fdcheckstd (struct proc *p); struct file *holdfp (struct filedesc *fdp, int fd, int flag); diff --git a/sys/sys/vfsops.h b/sys/sys/vfsops.h index b2ae193..6d80f34 100644 --- a/sys/sys/vfsops.h +++ b/sys/sys/vfsops.h @@ -193,12 +193,6 @@ struct vop_kqfilter_args { struct knote *a_kn; }; -struct vop_revoke_args { - struct vop_generic_args a_head; - struct vnode *a_vp; - int a_flags; -}; - struct vop_mmap_args { struct vop_generic_args a_head; struct vnode *a_vp; @@ -593,7 +587,7 @@ struct vop_ops { int (*vop_ioctl)(struct vop_ioctl_args *); int (*vop_poll)(struct vop_poll_args *); int (*vop_kqfilter)(struct vop_kqfilter_args *); - int (*vop_revoke)(struct vop_revoke_args *); + int (*vop_unused01)(void *); /* was vop_revoke */ int (*vop_mmap)(struct vop_mmap_args *); int (*vop_fsync)(struct vop_fsync_args *); int (*vop_old_remove)(struct vop_old_remove_args *); @@ -672,7 +666,6 @@ union vop_args_union { struct vop_ioctl_args vu_ioctl; struct vop_poll_args vu_poll; struct vop_kqfilter_args vu_kqfilter; - struct vop_revoke_args vu_revoke; struct vop_mmap_args vu_mmap; struct vop_fsync_args vu_fsync; struct vop_old_remove_args vu_remove; @@ -753,7 +746,6 @@ int vop_ioctl(struct vop_ops *ops, struct vnode *vp, u_long command, int vop_poll(struct vop_ops *ops, struct vnode *vp, int events, struct ucred *cred); int vop_kqfilter(struct vop_ops *ops, struct vnode *vp, struct knote *kn); -int vop_revoke(struct vop_ops *ops, struct vnode *vp, int flags); int vop_mmap(struct vop_ops *ops, struct vnode *vp, int fflags, struct ucred *cred); int vop_fsync(struct vop_ops *ops, struct vnode *vp, int waitfor); @@ -864,7 +856,6 @@ int vop_write_ap(struct vop_write_args *ap); int vop_ioctl_ap(struct vop_ioctl_args *ap); int vop_poll_ap(struct vop_poll_args *ap); int vop_kqfilter_ap(struct vop_kqfilter_args *ap); -int vop_revoke_ap(struct vop_revoke_args *ap); int vop_mmap_ap(struct vop_mmap_args *ap); int vop_fsync_ap(struct vop_fsync_args *ap); int vop_old_remove_ap(struct vop_old_remove_args *ap); @@ -927,7 +918,6 @@ extern struct syslink_desc vop_write_desc; extern struct syslink_desc vop_ioctl_desc; extern struct syslink_desc vop_poll_desc; extern struct syslink_desc vop_kqfilter_desc; -extern struct syslink_desc vop_revoke_desc; extern struct syslink_desc vop_mmap_desc; extern struct syslink_desc vop_fsync_desc; extern struct syslink_desc vop_old_remove_desc; @@ -996,8 +986,6 @@ extern struct syslink_desc vop_nrename_desc; vop_poll(*(vp)->v_ops, vp, events, cred) #define VOP_KQFILTER(vp, kn) \ vop_kqfilter(*(vp)->v_ops, vp, kn) -#define VOP_REVOKE(vp, flags) \ - vop_revoke(*(vp)->v_ops, vp, flags) #define VOP_MMAP(vp, fflags, cred) \ vop_mmap(*(vp)->v_ops, vp, fflags, cred) #define VOP_FSYNC(vp, waitfor) \ diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index 70c7ac4..f278cb8 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -358,7 +358,6 @@ extern int vttoif_tab[]; #define WRITECLOSE 0x0004 /* vflush: only close writable files */ #define DOCLOSE 0x0008 /* vclean: close active files */ #define V_SAVE 0x0001 /* vinvalbuf: sync file first */ -#define REVOKEALL 0x0001 /* vop_revoke: revoke all aliases */ #ifdef DIAGNOSTIC #define VATTR_NULL(vap) vattr_null(vap) @@ -479,6 +478,7 @@ void vclean_vxlocked (struct vnode *vp, int flags); void vclean_unlocked (struct vnode *vp); void vgone_vxlocked (struct vnode *vp); void vupdatefsmid (struct vnode *vp); +int vrevoke (struct vnode *vp, struct ucred *cred); int vinvalbuf (struct vnode *vp, int save, int slpflag, int slptimeo); int vtruncbuf (struct vnode *vp, off_t length, int blksize); int vfsync(struct vnode *vp, int waitfor, int passes, @@ -528,7 +528,6 @@ int vop_stdmarkatime(struct vop_markatime_args *ap); int vop_nopoll (struct vop_poll_args *ap); int vop_stdpathconf (struct vop_pathconf_args *ap); int vop_stdpoll (struct vop_poll_args *ap); -int vop_stdrevoke (struct vop_revoke_args *ap); int vop_eopnotsupp (struct vop_generic_args *ap); int vop_ebadf (struct vop_generic_args *ap); int vop_einval (struct vop_generic_args *ap); diff --git a/sys/vfs/specfs/spec_vnops.c b/sys/vfs/specfs/spec_vnops.c index 97b5cf9..91fbbfd 100644 --- a/sys/vfs/specfs/spec_vnops.c +++ b/sys/vfs/specfs/spec_vnops.c @@ -688,7 +688,7 @@ spec_bmap(struct vop_bmap_args *ap) * * spec_close(struct vnode *a_vp, int a_fflag) * - * NOTE: the vnode may or may not be locked on call. + * NOTE: The vnode may or may not be locked on call. */ /* ARGSUSED */ static int @@ -701,37 +701,35 @@ spec_close(struct vop_close_args *ap) int needrelock; /* - * Hack: a tty device that is a controlling terminal - * has a reference from the session structure. - * We cannot easily tell that a character device is - * a controlling terminal, unless it is the closing - * process' controlling terminal. In that case, - * if the reference count is 2 (this last descriptor - * plus the session), release the reference from the session. + * A couple of hacks for devices and tty devices. The + * vnode ref count cannot be used to figure out the + * last close, but we can use v_opencount now that + * revoke works properly. * - * It is possible for v_opencount to be 0 or 1 in this case, 0 - * because the tty might have been revoked. + * Detect the last close on a controlling terminal and clear + * the session (half-close). */ if (dev) reference_dev(dev); - if (vcount(vp) == 2 && vp->v_opencount <= 1 && - p && vp == p->p_session->s_ttyvp) { + + if (p && vp->v_opencount <= 1 && vp == p->p_session->s_ttyvp) { p->p_session->s_ttyvp = NULL; vrele(vp); } /* - * Vnodes can be opened and close multiple times. Do not really + * Vnodes can be opened and closed multiple times. Do not really * close the device unless (1) it is being closed forcibly, * (2) the device wants to track closes, or (3) this is the last * vnode doing its last close on the device. * * XXX the VXLOCK (force close) case can leave vnodes referencing - * a closed device. + * a closed device. This might not occur now that our revoke is + * fixed. */ if (dev && ((vp->v_flag & VRECLAIMED) || (dev_dflags(dev) & D_TRACKCLOSE) || - (vcount(vp) <= 1 && vp->v_opencount == 1))) { + (vp->v_opencount == 1))) { needrelock = 0; if (vn_islocked(vp)) { needrelock = 1; diff --git a/sys/vfs/union/union_vnops.c b/sys/vfs/union/union_vnops.c index 60c7962..ae6378c 100644 --- a/sys/vfs/union/union_vnops.c +++ b/sys/vfs/union/union_vnops.c @@ -94,7 +94,6 @@ static int union_readlink (struct vop_readlink_args *ap); static int union_reclaim (struct vop_reclaim_args *ap); static int union_remove (struct vop_old_remove_args *ap); static int union_rename (struct vop_old_rename_args *ap); -static int union_revoke (struct vop_revoke_args *ap); static int union_rmdir (struct vop_old_rmdir_args *ap); static int union_poll (struct vop_poll_args *ap); static int union_setattr (struct vop_setattr_args *ap); @@ -1143,29 +1142,6 @@ union_poll(struct vop_poll_args *ap) } /* - * union_revoke(struct vnode *a_vp, int a_flags, struct thread *a_td) - */ -static int -union_revoke(struct vop_revoke_args *ap) -{ - struct vnode *vp = ap->a_vp; - struct vnode *vx; - - if ((vx = UPPERVP(vp)) != NULL) { - vx_get(vx); - VOP_REVOKE(vx, ap->a_flags); - vx_put(vx); - } - if ((vx = LOWERVP(vp)) != NULL) { - vx_get(vx); - VOP_REVOKE(vx, ap->a_flags); - vx_put(vx); - } - vgone_vxlocked(vp); - return (0); -} - -/* * union_mmap(struct vnode *a_vp, int a_fflags, struct ucred *a_cred, * struct thread *a_td) */ @@ -1804,7 +1780,6 @@ struct vop_ops union_vnode_vops = { .vop_reclaim = union_reclaim, .vop_old_remove = union_remove, .vop_old_rename = union_rename, - .vop_revoke = union_revoke, .vop_old_rmdir = union_rmdir, .vop_setattr = union_setattr, .vop_strategy = union_strategy,