Index: kern/vfs_vnops.c =================================================================== --- kern/vfs_vnops.c (revision 225519) +++ kern/vfs_vnops.c (working copy) @@ -95,7 +95,8 @@ vn_open(ndp, flagp, cmode, fp) } /* - * Common code for vnode open operations. + * Common code for vnode open operations via a name lookup. + * Lookup the vnode and invoke VOP_CREATE if needed. * Check permissions, and call the VOP_OPEN or VOP_CREATE routine. * * Note that this does NOT free nameidata for the successful case, @@ -111,7 +112,6 @@ vn_open_cred(struct nameidata *ndp, int *flagp, in struct vattr vat; struct vattr *vap = &vat; int fmode, error; - accmode_t accmode; int vfslocked, mpsafe; mpsafe = ndp->ni_cnd.cn_flags & MPSAFE; @@ -192,20 +192,44 @@ restart: vfslocked = NDHASGIANT(ndp); vp = ndp->ni_vp; } - if (vp->v_type == VLNK) { - error = EMLINK; + error = vn_open_vnode(vp, fmode, cred, td, fp); + if (error) goto bad; - } - if (vp->v_type == VSOCK) { - error = EOPNOTSUPP; - goto bad; - } + *flagp = fmode; + if (!mpsafe) + VFS_UNLOCK_GIANT(vfslocked); + return (0); +bad: + NDFREE(ndp, NDF_ONLY_PNBUF); + vput(vp); + VFS_UNLOCK_GIANT(vfslocked); + *flagp = fmode; + ndp->ni_vp = NULL; + return (error); +} + +/* + * Common code for vnode open operations once a vnode is located. + * Check permissions, and call the VOP_OPEN routine. + */ +int +vn_open_vnode(struct vnode *vp, int fmode, struct ucred *cred, + struct thread *td, struct file *fp) +{ + struct mount *mp; + accmode_t accmode; + struct flock lf; + int error, have_flock, lock_flags, type; + + VFS_ASSERT_GIANT(vp->v_mount); + if (vp->v_type == VLNK) + return (EMLINK); + if (vp->v_type == VSOCK) + return (EOPNOTSUPP); accmode = 0; if (fmode & (FWRITE | O_TRUNC)) { - if (vp->v_type == VDIR) { - error = EISDIR; - goto bad; - } + if (vp->v_type == VDIR) + return (EISDIR); accmode |= VWRITE; } if (fmode & FREAD) @@ -217,40 +241,83 @@ restart: #ifdef MAC error = mac_vnode_check_open(cred, vp, accmode); if (error) - goto bad; + return (error); #endif if ((fmode & O_CREAT) == 0) { if (accmode & VWRITE) { error = vn_writechk(vp); if (error) - goto bad; + return (error); } if (accmode) { error = VOP_ACCESS(vp, accmode, cred, td); if (error) - goto bad; + return (error); } } if ((error = VOP_OPEN(vp, fmode, cred, td, fp)) != 0) - goto bad; + return (error); + if (fmode & (O_EXLOCK | O_SHLOCK)) { + KASSERT(fp != NULL, ("open with flock requires fp")); + lock_flags = VOP_ISLOCKED(vp); + VOP_UNLOCK(vp, 0); + lf.l_whence = SEEK_SET; + lf.l_start = 0; + lf.l_len = 0; + if (fmode & O_EXLOCK) + lf.l_type = F_WRLCK; + else + lf.l_type = F_RDLCK; + type = F_FLOCK; + if ((fmode & FNONBLOCK) == 0) + type |= F_WAIT; + error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type); + have_flock = (error == 0); +#if 0 + if (have_flock) + printf("Got the lock on vp %p\n", vp); +#endif + vn_lock(vp, lock_flags | LK_RETRY); + if (error == 0 && vp->v_iflag & VI_DOOMED) + error = ENOENT; + /* + * Another thread might have used this vnode as an + * executable while the vnode lock was dropped. + * Ensure the vnode is still able to be opened for + * writing after the lock has been obtained. + */ + if (error == 0 && accmode & VWRITE) + error = vn_writechk(vp); + if (error) { + VOP_UNLOCK(vp, 0); + if (have_flock) { + lf.l_whence = SEEK_SET; + lf.l_start = 0; + lf.l_len = 0; + lf.l_type = F_UNLCK; + (void) VOP_ADVLOCK(vp, fp, F_UNLCK, &lf, + F_FLOCK); + } + vn_start_write(vp, &mp, V_WAIT); + vn_lock(vp, lock_flags | LK_RETRY); + (void)VOP_CLOSE(vp, fmode, cred, td); + vn_finished_write(mp); + return (error); + } + KASSERT(have_flock, ("darn well better have the flock")); +#if 0 + printf("Setting FHASLOCK on fp %p\n", fp); +#endif + atomic_set_int(&fp->f_flag, FHASLOCK); + } if (fmode & FWRITE) { vp->v_writecount++; CTR3(KTR_VFS, "%s: vp %p v_writecount increased to %d", __func__, vp, vp->v_writecount); } - *flagp = fmode; - ASSERT_VOP_LOCKED(vp, "vn_open_cred"); - if (!mpsafe) - VFS_UNLOCK_GIANT(vfslocked); + ASSERT_VOP_LOCKED(vp, "vn_open_vnode"); return (0); -bad: - NDFREE(ndp, NDF_ONLY_PNBUF); - vput(vp); - VFS_UNLOCK_GIANT(vfslocked); - *flagp = fmode; - ndp->ni_vp = NULL; - return (error); } /* @@ -975,19 +1042,22 @@ vn_closefile(fp, td) int error; vp = fp->f_vnode; + fp->f_ops = &badfileops; vfslocked = VFS_LOCK_GIANT(vp->v_mount); + if (fp->f_type == DTYPE_VNODE && fp->f_flag & FHASLOCK) + vref(vp); + + error = vn_close(vp, fp->f_flag, fp->f_cred, td); + if (fp->f_type == DTYPE_VNODE && fp->f_flag & FHASLOCK) { lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; lf.l_type = F_UNLCK; (void) VOP_ADVLOCK(vp, fp, F_UNLCK, &lf, F_FLOCK); + vrele(vp); } - - fp->f_ops = &badfileops; - - error = vn_close(vp, fp->f_flag, fp->f_cred, td); VFS_UNLOCK_GIANT(vfslocked); return (error); } Index: kern/vfs_syscalls.c =================================================================== --- kern/vfs_syscalls.c (revision 225519) +++ kern/vfs_syscalls.c (working copy) @@ -1051,8 +1051,7 @@ kern_openat(struct thread *td, int fd, char *path, struct vnode *vp; int cmode; struct file *nfp; - int type, indx, error; - struct flock lf; + int indx, error; struct nameidata nd; int vfslocked; @@ -1137,26 +1136,11 @@ kern_openat(struct thread *td, int fd, char *path, if (fp->f_ops == &badfileops) { KASSERT(vp->v_type != VFIFO, ("Unexpected fifo.")); fp->f_seqcount = 1; - finit(fp, flags & FMASK, DTYPE_VNODE, vp, &vnops); + finit(fp, (flags & FMASK) | (fp->f_flag & FHASLOCK), DTYPE_VNODE, + vp, &vnops); } VOP_UNLOCK(vp, 0); - if (fp->f_type == DTYPE_VNODE && (flags & (O_EXLOCK | O_SHLOCK)) != 0) { - lf.l_whence = SEEK_SET; - lf.l_start = 0; - lf.l_len = 0; - if (flags & O_EXLOCK) - lf.l_type = F_WRLCK; - else - lf.l_type = F_RDLCK; - type = F_FLOCK; - if ((flags & FNONBLOCK) == 0) - type |= F_WAIT; - if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, - type)) != 0) - goto bad; - atomic_set_int(&fp->f_flag, FHASLOCK); - } if (flags & O_TRUNC) { error = fo_truncate(fp, 0, td->td_ucred, td); if (error) @@ -4368,13 +4356,9 @@ fhopen(td, uap) struct mount *mp; struct vnode *vp; struct fhandle fhp; - struct vattr vat; - struct vattr *vap = &vat; - struct flock lf; struct file *fp; register struct filedesc *fdp = p->p_fd; - int fmode, error, type; - accmode_t accmode; + int fmode, error; struct file *nfp; int vfslocked; int indx; @@ -4397,147 +4381,67 @@ fhopen(td, uap) /* now give me my vnode, it gets returned to me locked */ error = VFS_FHTOVP(mp, &fhp.fh_fid, &vp); vfs_unbusy(mp); - if (error) - goto out; - /* - * from now on we have to make sure not - * to forget about the vnode - * any error that causes an abort must vput(vp) - * just set error = err and 'goto bad;'. - */ + if (error) { + VFS_UNLOCK_GIANT(vfslocked); + return (error); + } - /* - * from vn_open - */ - if (vp->v_type == VLNK) { - error = EMLINK; - goto bad; + error = fallocf(td, &nfp, &indx, fmode); + if (error) { + vput(vp); + VFS_UNLOCK_GIANT(vfslocked); + return (error); } - if (vp->v_type == VSOCK) { - error = EOPNOTSUPP; - goto bad; - } - accmode = 0; - if (fmode & (FWRITE | O_TRUNC)) { - if (vp->v_type == VDIR) { - error = EISDIR; - goto bad; - } - error = vn_writechk(vp); - if (error) - goto bad; - accmode |= VWRITE; - } - if (fmode & FREAD) - accmode |= VREAD; - if ((fmode & O_APPEND) && (fmode & FWRITE)) - accmode |= VAPPEND; -#ifdef MAC - error = mac_vnode_check_open(td->td_ucred, vp, accmode); - if (error) - goto bad; + + /* An extra reference on `nfp' has been held for us by falloc(). */ + fp = nfp; +#ifdef INVARIANTS + td->td_dupfd = -1; #endif - if (accmode) { - error = VOP_ACCESS(vp, accmode, td->td_ucred, td); - if (error) - goto bad; - } - if (fmode & O_TRUNC) { - vfs_ref(mp); - VOP_UNLOCK(vp, 0); /* XXX */ - if ((error = vn_start_write(NULL, &mp, V_WAIT | PCATCH)) != 0) { - vrele(vp); - vfs_rel(mp); - goto out; - } - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); /* XXX */ - vfs_rel(mp); -#ifdef MAC + error = vn_open_vnode(vp, fmode, td->td_ucred, td, fp); + if (error) { + vput(vp); + VFS_UNLOCK_GIANT(vfslocked); + + KASSERT(fp->f_ops == &badfileops, + ("VOP_OPEN in fhopen() set f_ops")); + KASSERT(td->td_dupfd < 0, + ("fhopen() encountered fdopen()")); /* - * We don't yet have fp->f_cred, so use td->td_ucred, which - * should be right. + * Clean up the descriptor, but only if another thread hadn't + * replaced or closed it. */ - error = mac_vnode_check_write(td->td_ucred, td->td_ucred, vp); - if (error == 0) { + fdclose(fdp, fp, indx, td); + fdrop(fp, td); +#if 0 + /* XXX: Open does this. */ + if (error == ERESTART) + error = EINTR; #endif - VATTR_NULL(vap); - vap->va_size = 0; - error = VOP_SETATTR(vp, vap, td->td_ucred); -#ifdef MAC - } -#endif - vn_finished_write(mp); - if (error) - goto bad; + return (error); } - error = VOP_OPEN(vp, fmode, td->td_ucred, td, NULL); - if (error) - goto bad; - - if (fmode & FWRITE) { - vp->v_writecount++; - CTR3(KTR_VFS, "%s: vp %p v_writecount increased to %d", - __func__, vp, vp->v_writecount); - } - - /* - * end of vn_open code - */ - - if ((error = fallocf(td, &nfp, &indx, fmode)) != 0) { - if (fmode & FWRITE) { - vp->v_writecount--; - CTR3(KTR_VFS, "%s: vp %p v_writecount decreased to %d", - __func__, vp, vp->v_writecount); - } - goto bad; - } - /* An extra reference on `nfp' has been held for us by falloc(). */ - fp = nfp; +#ifdef INVARIANTS + td->td_dupfd = 0; +#endif nfp->f_vnode = vp; - finit(nfp, fmode & FMASK, DTYPE_VNODE, vp, &vnops); - if (fmode & (O_EXLOCK | O_SHLOCK)) { - lf.l_whence = SEEK_SET; - lf.l_start = 0; - lf.l_len = 0; - if (fmode & O_EXLOCK) - lf.l_type = F_WRLCK; - else - lf.l_type = F_RDLCK; - type = F_FLOCK; - if ((fmode & FNONBLOCK) == 0) - type |= F_WAIT; - VOP_UNLOCK(vp, 0); - if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, - type)) != 0) { - /* - * The lock request failed. Normally close the - * descriptor but handle the case where someone might - * have dup()d or close()d it when we weren't looking. - */ + finit(fp, (fmode & FMASK) | (fp->f_flag & FHASLOCK), DTYPE_VNODE, vp, + &vnops); + VOP_UNLOCK(vp, 0); + if (fmode & O_TRUNC) { + error = fo_truncate(fp, 0, td->td_ucred, td); + if (error) { + vrele(vp); + VFS_UNLOCK_GIANT(vfslocked); fdclose(fdp, fp, indx, td); - - /* - * release our private reference - */ fdrop(fp, td); - goto out; + return (error); } - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); - atomic_set_int(&fp->f_flag, FHASLOCK); } - VOP_UNLOCK(vp, 0); fdrop(fp, td); VFS_UNLOCK_GIANT(vfslocked); td->td_retval[0] = indx; return (0); - -bad: - vput(vp); -out: - VFS_UNLOCK_GIANT(vfslocked); - return (error); } /* Index: sys/vnode.h =================================================================== --- sys/vnode.h (revision 225519) +++ sys/vnode.h (working copy) @@ -650,6 +650,8 @@ int _vn_lock(struct vnode *vp, int flags, char *fi int vn_open(struct nameidata *ndp, int *flagp, int cmode, struct file *fp); int vn_open_cred(struct nameidata *ndp, int *flagp, int cmode, u_int vn_open_flags, struct ucred *cred, struct file *fp); +int vn_open_vnode(struct vnode *vp, int fmode, struct ucred *cred, + struct thread *td, struct file *fp); int vn_pollrecord(struct vnode *vp, struct thread *p, int events); int vn_rdwr(enum uio_rw rw, struct vnode *vp, void *base, int len, off_t offset, enum uio_seg segflg, int ioflg,