aboutsummaryrefslogtreecommitdiff
path: root/sys/kern
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2019-05-05 11:20:43 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2019-05-05 11:20:43 +0000
commit78022527bb7aad6015ea88102562ad6ede2752fa (patch)
treeaa5f2c3500763d961ae6e99d0d1ef55d30dd62de /sys/kern
parent7f1446052fd7ff1006f9a98a76f8b1010e4e413e (diff)
downloadsrc-78022527bb7aad6015ea88102562ad6ede2752fa.tar.gz
src-78022527bb7aad6015ea88102562ad6ede2752fa.zip
Switch to use shared vnode locks for text files during image activation.
kern_execve() locks text vnode exclusive to be able to set and clear VV_TEXT flag. VV_TEXT is mutually exclusive with the v_writecount > 0 condition. The change removes VV_TEXT, replacing it with the condition v_writecount <= -1, and puts v_writecount under the vnode interlock. Each text reference decrements v_writecount. To clear the text reference when the segment is unmapped, it is recorded in the vm_map_entry backed by the text file as MAP_ENTRY_VN_TEXT flag, and v_writecount is incremented on the map entry removal The operations like VOP_ADD_WRITECOUNT() and VOP_SET_TEXT() check that v_writecount does not contradict the desired change. vn_writecheck() is now racy and its use was eliminated everywhere except access. Atomic check for writeability and increment of v_writecount is performed by the VOP. vn_truncate() now increments v_writecount around VOP_SETATTR() call, lack of which is arguably a bug on its own. nullfs bypasses v_writecount to the lower vnode always, so nullfs vnode has its own v_writecount correct, and lower vnode gets all references, since object->handle is always lower vnode. On the text vnode' vm object dealloc, the v_writecount value is reset to zero, and deadfs vop_unset_text short-circuit the operation. Reclamation of lowervp always reclaims all nullfs vnodes referencing lowervp first, so no stray references are left. Reviewed by: markj, trasz Tested by: mjg, pho Sponsored by: The FreeBSD Foundation MFC after: 1 month Differential revision: https://reviews.freebsd.org/D19923
Notes
Notes: svn path=/head/; revision=347151
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/imgact_aout.c12
-rw-r--r--sys/kern/imgact_elf.c60
-rw-r--r--sys/kern/kern_exec.c51
-rw-r--r--sys/kern/vfs_default.c59
-rw-r--r--sys/kern/vfs_subr.c4
-rw-r--r--sys/kern/vfs_vnops.c116
-rw-r--r--sys/kern/vnode_if.src14
7 files changed, 150 insertions, 166 deletions
diff --git a/sys/kern/imgact_aout.c b/sys/kern/imgact_aout.c
index c8989fd55c74..0ca39b69d908 100644
--- a/sys/kern/imgact_aout.c
+++ b/sys/kern/imgact_aout.c
@@ -247,8 +247,8 @@ exec_aout_imgact(struct image_params *imgp)
/* data + bss can't exceed rlimit */
a_out->a_data + bss_size > lim_cur_proc(imgp->proc, RLIMIT_DATA) ||
racct_set(imgp->proc, RACCT_DATA, a_out->a_data + bss_size) != 0) {
- PROC_UNLOCK(imgp->proc);
- return (ENOMEM);
+ PROC_UNLOCK(imgp->proc);
+ return (ENOMEM);
}
PROC_UNLOCK(imgp->proc);
@@ -267,7 +267,7 @@ exec_aout_imgact(struct image_params *imgp)
*/
error = exec_new_vmspace(imgp, &aout_sysvec);
- vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY);
+ vn_lock(imgp->vp, LK_SHARED | LK_RETRY);
if (error)
return (error);
@@ -286,12 +286,13 @@ exec_aout_imgact(struct image_params *imgp)
file_offset,
virtual_offset, text_end,
VM_PROT_READ | VM_PROT_EXECUTE, VM_PROT_ALL,
- MAP_COPY_ON_WRITE | MAP_PREFAULT);
+ MAP_COPY_ON_WRITE | MAP_PREFAULT | MAP_VN_EXEC);
if (error) {
vm_map_unlock(map);
vm_object_deallocate(object);
return (error);
}
+ VOP_SET_TEXT_CHECKED(imgp->vp);
data_end = text_end + a_out->a_data;
if (a_out->a_data) {
vm_object_reference(object);
@@ -299,12 +300,13 @@ exec_aout_imgact(struct image_params *imgp)
file_offset + a_out->a_text,
text_end, data_end,
VM_PROT_ALL, VM_PROT_ALL,
- MAP_COPY_ON_WRITE | MAP_PREFAULT);
+ MAP_COPY_ON_WRITE | MAP_PREFAULT | MAP_VN_EXEC);
if (error) {
vm_map_unlock(map);
vm_object_deallocate(object);
return (error);
}
+ VOP_SET_TEXT_CHECKED(imgp->vp);
}
if (bss_size) {
diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c
index 0802b820095a..5e198abaad48 100644
--- a/sys/kern/imgact_elf.c
+++ b/sys/kern/imgact_elf.c
@@ -526,13 +526,17 @@ __elfN(map_insert)(struct image_params *imgp, vm_map_t map, vm_object_t object,
} else {
vm_object_reference(object);
rv = vm_map_fixed(map, object, offset, start, end - start,
- prot, VM_PROT_ALL, cow | MAP_CHECK_EXCL);
+ prot, VM_PROT_ALL, cow | MAP_CHECK_EXCL |
+ (object != NULL ? MAP_VN_EXEC : 0));
if (rv != KERN_SUCCESS) {
locked = VOP_ISLOCKED(imgp->vp);
VOP_UNLOCK(imgp->vp, 0);
vm_object_deallocate(object);
vn_lock(imgp->vp, locked | LK_RETRY);
return (rv);
+ } else if (object != NULL) {
+ MPASS(imgp->vp->v_object == object);
+ VOP_SET_TEXT_CHECKED(imgp->vp);
}
}
return (KERN_SUCCESS);
@@ -589,13 +593,8 @@ __elfN(load_section)(struct image_params *imgp, vm_ooffset_t offset,
cow = MAP_COPY_ON_WRITE | MAP_PREFAULT |
(prot & VM_PROT_WRITE ? 0 : MAP_DISABLE_COREDUMP);
- rv = __elfN(map_insert)(imgp, map,
- object,
- file_addr, /* file offset */
- map_addr, /* virtual start */
- map_addr + map_len,/* virtual end */
- prot,
- cow);
+ rv = __elfN(map_insert)(imgp, map, object, file_addr,
+ map_addr, map_addr + map_len, prot, cow);
if (rv != KERN_SUCCESS)
return (EINVAL);
@@ -716,7 +715,7 @@ __elfN(load_file)(struct proc *p, const char *file, u_long *addr,
struct nameidata *nd;
struct vattr *attr;
struct image_params *imgp;
- u_long flags, rbase;
+ u_long rbase;
u_long base_addr = 0;
int error;
@@ -744,10 +743,8 @@ __elfN(load_file)(struct proc *p, const char *file, u_long *addr,
imgp->object = NULL;
imgp->execlabel = NULL;
- flags = FOLLOW | LOCKSHARED | LOCKLEAF;
-
-again:
- NDINIT(nd, LOOKUP, flags, UIO_SYSSPACE, file, curthread);
+ NDINIT(nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF, UIO_SYSSPACE, file,
+ curthread);
if ((error = namei(nd)) != 0) {
nd->ni_vp = NULL;
goto fail;
@@ -762,27 +759,6 @@ again:
if (error)
goto fail;
- /*
- * Also make certain that the interpreter stays the same,
- * so set its VV_TEXT flag, too. Since this function is only
- * used to load the interpreter, the VV_TEXT is almost always
- * already set.
- */
- if (VOP_IS_TEXT(nd->ni_vp) == 0) {
- if (VOP_ISLOCKED(nd->ni_vp) != LK_EXCLUSIVE) {
- /*
- * LK_UPGRADE could have resulted in dropping
- * the lock. Just try again from the start,
- * this time with exclusive vnode lock.
- */
- vput(nd->ni_vp);
- flags &= ~LOCKSHARED;
- goto again;
- }
-
- VOP_SET_TEXT(nd->ni_vp);
- }
-
error = exec_map_first_page(imgp);
if (error)
goto fail;
@@ -825,9 +801,11 @@ fail:
if (imgp->firstpage)
exec_unmap_first_page(imgp);
- if (nd->ni_vp)
+ if (nd->ni_vp) {
+ if (imgp->textset)
+ VOP_UNSET_TEXT_CHECKED(nd->ni_vp);
vput(nd->ni_vp);
-
+ }
free(tempdata, M_TEMP);
return (error);
@@ -961,7 +939,7 @@ __elfN(get_interp)(struct image_params *imgp, const Elf_Phdr *phdr,
if (interp == NULL) {
VOP_UNLOCK(imgp->vp, 0);
interp = malloc(interp_name_len + 1, M_TEMP, M_WAITOK);
- vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY);
+ vn_lock(imgp->vp, LK_SHARED | LK_RETRY);
}
error = vn_rdwr(UIO_READ, imgp->vp, interp,
interp_name_len, phdr->p_offset,
@@ -1228,7 +1206,7 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp)
maxv / 2, 1UL << flsl(maxalign));
}
- vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY);
+ vn_lock(imgp->vp, LK_SHARED | LK_RETRY);
if (error != 0)
goto ret;
@@ -1272,7 +1250,7 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp)
}
error = __elfN(load_interp)(imgp, brand_info, interp, &addr,
&imgp->entry_addr);
- vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY);
+ vn_lock(imgp->vp, LK_SHARED | LK_RETRY);
if (error != 0)
goto ret;
} else
@@ -1285,7 +1263,7 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp)
if (elf_auxargs == NULL) {
VOP_UNLOCK(imgp->vp, 0);
elf_auxargs = malloc(sizeof(Elf_Auxargs), M_TEMP, M_WAITOK);
- vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY);
+ vn_lock(imgp->vp, LK_SHARED | LK_RETRY);
}
elf_auxargs->execfd = -1;
elf_auxargs->phdr = proghdr + et_dyn_addr;
@@ -2570,7 +2548,7 @@ __elfN(parse_notes)(struct image_params *imgp, Elf_Note *checknote,
if (buf == NULL) {
VOP_UNLOCK(imgp->vp, 0);
buf = malloc(pnote->p_filesz, M_TEMP, M_WAITOK);
- vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY);
+ vn_lock(imgp->vp, LK_SHARED | LK_RETRY);
}
error = vn_rdwr(UIO_READ, imgp->vp, buf, pnote->p_filesz,
pnote->p_offset, UIO_SYSSPACE, IO_NODELOCKED,
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c
index c42f30f0e345..61ec0507e855 100644
--- a/sys/kern/kern_exec.c
+++ b/sys/kern/kern_exec.c
@@ -375,7 +375,6 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p)
#endif
struct vnode *oldtextvp = NULL, *newtextvp;
int credential_changing;
- int textset;
#ifdef MAC
struct label *interpvplabel = NULL;
int will_transition;
@@ -423,8 +422,8 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p)
* interpreter if this is an interpreted binary.
*/
if (args->fname != NULL) {
- NDINIT(&nd, LOOKUP, ISOPEN | LOCKLEAF | FOLLOW | SAVENAME
- | AUDITVNODE1, UIO_SYSSPACE, args->fname, td);
+ NDINIT(&nd, LOOKUP, ISOPEN | LOCKLEAF | LOCKSHARED | FOLLOW |
+ SAVENAME | AUDITVNODE1, UIO_SYSSPACE, args->fname, td);
}
SDT_PROBE1(proc, , , exec, args->fname);
@@ -457,13 +456,14 @@ interpret:
error = fgetvp_exec(td, args->fd, &cap_fexecve_rights, &newtextvp);
if (error)
goto exec_fail;
- vn_lock(newtextvp, LK_EXCLUSIVE | LK_RETRY);
+ vn_lock(newtextvp, LK_SHARED | LK_RETRY);
AUDIT_ARG_VNODE1(newtextvp);
imgp->vp = newtextvp;
}
/*
- * Check file permissions (also 'opens' file)
+ * Check file permissions. Also 'opens' file and sets its vnode to
+ * text mode.
*/
error = exec_check_permissions(imgp);
if (error)
@@ -473,16 +473,6 @@ interpret:
if (imgp->object != NULL)
vm_object_reference(imgp->object);
- /*
- * Set VV_TEXT now so no one can write to the executable while we're
- * activating it.
- *
- * Remember if this was set before and unset it in case this is not
- * actually an executable image.
- */
- textset = VOP_IS_TEXT(imgp->vp);
- VOP_SET_TEXT(imgp->vp);
-
error = exec_map_first_page(imgp);
if (error)
goto exec_fail_dealloc;
@@ -610,11 +600,8 @@ interpret:
}
if (error) {
- if (error == -1) {
- if (textset == 0)
- VOP_UNSET_TEXT(imgp->vp);
+ if (error == -1)
error = ENOEXEC;
- }
goto exec_fail_dealloc;
}
@@ -625,12 +612,13 @@ interpret:
if (imgp->interpreted) {
exec_unmap_first_page(imgp);
/*
- * VV_TEXT needs to be unset for scripts. There is a short
- * period before we determine that something is a script where
- * VV_TEXT will be set. The vnode lock is held over this
- * entire period so nothing should illegitimately be blocked.
+ * The text reference needs to be removed for scripts.
+ * There is a short period before we determine that
+ * something is a script where text reference is active.
+ * The vnode lock is held over this entire period
+ * so nothing should illegitimately be blocked.
*/
- VOP_UNSET_TEXT(imgp->vp);
+ VOP_UNSET_TEXT_CHECKED(imgp->vp);
/* free name buffer and old vnode */
if (args->fname != NULL)
NDFREE(&nd, NDF_ONLY_PNBUF);
@@ -886,6 +874,8 @@ exec_fail_dealloc:
NDFREE(&nd, NDF_ONLY_PNBUF);
if (imgp->opened)
VOP_CLOSE(imgp->vp, FREAD, td->td_ucred, td);
+ if (imgp->textset)
+ VOP_UNSET_TEXT_CHECKED(imgp->vp);
if (error != 0)
vput(imgp->vp);
else
@@ -1706,7 +1696,7 @@ exec_check_permissions(struct image_params *imgp)
struct vnode *vp = imgp->vp;
struct vattr *attr = imgp->attr;
struct thread *td;
- int error, writecount;
+ int error;
td = curthread;
@@ -1750,12 +1740,17 @@ exec_check_permissions(struct image_params *imgp)
/*
* Check number of open-for-writes on the file and deny execution
* if there are any.
+ *
+ * Add a text reference now so no one can write to the
+ * executable while we're activating it.
+ *
+ * Remember if this was set before and unset it in case this is not
+ * actually an executable image.
*/
- error = VOP_GET_WRITECOUNT(vp, &writecount);
+ error = VOP_SET_TEXT(vp);
if (error != 0)
return (error);
- if (writecount != 0)
- return (ETXTBSY);
+ imgp->textset = true;
/*
* Call filesystem specific open routine (which does nothing in the
diff --git a/sys/kern/vfs_default.c b/sys/kern/vfs_default.c
index 25ff153e2c92..47d0d4b2e0eb 100644
--- a/sys/kern/vfs_default.c
+++ b/sys/kern/vfs_default.c
@@ -81,9 +81,7 @@ static int dirent_exists(struct vnode *vp, const char *dirname,
#define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN+1) + 4)
static int vop_stdis_text(struct vop_is_text_args *ap);
-static int vop_stdset_text(struct vop_set_text_args *ap);
static int vop_stdunset_text(struct vop_unset_text_args *ap);
-static int vop_stdget_writecount(struct vop_get_writecount_args *ap);
static int vop_stdadd_writecount(struct vop_add_writecount_args *ap);
static int vop_stdfdatasync(struct vop_fdatasync_args *ap);
static int vop_stdgetpages_async(struct vop_getpages_async_args *ap);
@@ -141,7 +139,6 @@ struct vop_vector default_vnodeops = {
.vop_is_text = vop_stdis_text,
.vop_set_text = vop_stdset_text,
.vop_unset_text = vop_stdunset_text,
- .vop_get_writecount = vop_stdget_writecount,
.vop_add_writecount = vop_stdadd_writecount,
};
@@ -1070,39 +1067,63 @@ static int
vop_stdis_text(struct vop_is_text_args *ap)
{
- return ((ap->a_vp->v_vflag & VV_TEXT) != 0);
+ return (ap->a_vp->v_writecount < 0);
}
-static int
+int
vop_stdset_text(struct vop_set_text_args *ap)
{
+ struct vnode *vp;
+ int error;
- ap->a_vp->v_vflag |= VV_TEXT;
- return (0);
+ vp = ap->a_vp;
+ VI_LOCK(vp);
+ if (vp->v_writecount > 0) {
+ error = ETXTBSY;
+ } else {
+ vp->v_writecount--;
+ error = 0;
+ }
+ VI_UNLOCK(vp);
+ return (error);
}
static int
vop_stdunset_text(struct vop_unset_text_args *ap)
{
+ struct vnode *vp;
+ int error;
- ap->a_vp->v_vflag &= ~VV_TEXT;
- return (0);
-}
-
-static int
-vop_stdget_writecount(struct vop_get_writecount_args *ap)
-{
-
- *ap->a_writecount = ap->a_vp->v_writecount;
- return (0);
+ vp = ap->a_vp;
+ VI_LOCK(vp);
+ if (vp->v_writecount < 0) {
+ vp->v_writecount++;
+ error = 0;
+ } else {
+ error = EINVAL;
+ }
+ VI_UNLOCK(vp);
+ return (error);
}
static int
vop_stdadd_writecount(struct vop_add_writecount_args *ap)
{
+ struct vnode *vp;
+ int error;
- ap->a_vp->v_writecount += ap->a_inc;
- return (0);
+ vp = ap->a_vp;
+ VI_LOCK(vp);
+ if (vp->v_writecount < 0) {
+ error = ETXTBSY;
+ } else {
+ VNASSERT(vp->v_writecount + ap->a_inc >= 0, vp,
+ ("neg writecount increment %d", ap->a_inc));
+ vp->v_writecount += ap->a_inc;
+ error = 0;
+ }
+ VI_UNLOCK(vp);
+ return (error);
}
/*
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
index a6ac7bec13b7..7074e8d26f0f 100644
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -3491,8 +3491,6 @@ vn_printf(struct vnode *vp, const char *fmt, ...)
strlcat(buf, "|VV_ETERNALDEV", sizeof(buf));
if (vp->v_vflag & VV_CACHEDLABEL)
strlcat(buf, "|VV_CACHEDLABEL", sizeof(buf));
- if (vp->v_vflag & VV_TEXT)
- strlcat(buf, "|VV_TEXT", sizeof(buf));
if (vp->v_vflag & VV_COPYONWRITE)
strlcat(buf, "|VV_COPYONWRITE", sizeof(buf));
if (vp->v_vflag & VV_SYSTEM)
@@ -3508,7 +3506,7 @@ vn_printf(struct vnode *vp, const char *fmt, ...)
if (vp->v_vflag & VV_FORCEINSMQ)
strlcat(buf, "|VV_FORCEINSMQ", sizeof(buf));
flags = vp->v_vflag & ~(VV_ROOT | VV_ISTTY | VV_NOSYNC | VV_ETERNALDEV |
- VV_CACHEDLABEL | VV_TEXT | VV_COPYONWRITE | VV_SYSTEM | VV_PROCDEP |
+ VV_CACHEDLABEL | VV_COPYONWRITE | VV_SYSTEM | VV_PROCDEP |
VV_NOKNOTE | VV_DELETED | VV_MD | VV_FORCEINSMQ);
if (flags != 0) {
snprintf(buf2, sizeof(buf2), "|VV(0x%lx)", flags);
diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c
index 89b35c898977..a5ca6f4e7a6d 100644
--- a/sys/kern/vfs_vnops.c
+++ b/sys/kern/vfs_vnops.c
@@ -294,6 +294,39 @@ bad:
return (error);
}
+static int
+vn_open_vnode_advlock(struct vnode *vp, int fmode, struct file *fp)
+{
+ struct flock lf;
+ int error, lock_flags, type;
+
+ ASSERT_VOP_LOCKED(vp, "vn_open_vnode_advlock");
+ if ((fmode & (O_EXLOCK | O_SHLOCK)) == 0)
+ return (0);
+ KASSERT(fp != NULL, ("open with flock requires fp"));
+ if (fp->f_type != DTYPE_NONE && fp->f_type != DTYPE_VNODE)
+ return (EOPNOTSUPP);
+
+ lock_flags = VOP_ISLOCKED(vp);
+ VOP_UNLOCK(vp, 0);
+
+ lf.l_whence = SEEK_SET;
+ lf.l_start = 0;
+ lf.l_len = 0;
+ lf.l_type = (fmode & O_EXLOCK) != 0 ? F_WRLCK : F_RDLCK;
+ type = F_FLOCK;
+ if ((fmode & FNONBLOCK) == 0)
+ type |= F_WAIT;
+ error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type);
+ if (error == 0)
+ fp->f_flag |= FHASLOCK;
+
+ vn_lock(vp, lock_flags | LK_RETRY);
+ if (error == 0 && (vp->v_iflag & VI_DOOMED) != 0)
+ error = ENOENT;
+ return (error);
+}
+
/*
* Common code for vnode open operations once a vnode is located.
* Check permissions, and call the VOP_OPEN routine.
@@ -303,8 +336,7 @@ vn_open_vnode(struct vnode *vp, int fmode, struct ucred *cred,
struct thread *td, struct file *fp)
{
accmode_t accmode;
- struct flock lf;
- int error, lock_flags, type;
+ int error;
if (vp->v_type == VLNK)
return (EMLINK);
@@ -335,63 +367,31 @@ vn_open_vnode(struct vnode *vp, int fmode, struct ucred *cred,
accmode &= ~(VCREAT | VVERIFY);
#endif
- if ((fmode & O_CREAT) == 0) {
- if (accmode & VWRITE) {
- error = vn_writechk(vp);
- if (error)
- return (error);
- }
- if (accmode) {
- error = VOP_ACCESS(vp, accmode, cred, td);
- if (error)
- return (error);
- }
+ if ((fmode & O_CREAT) == 0 && accmode != 0) {
+ error = VOP_ACCESS(vp, accmode, cred, td);
+ if (error != 0)
+ return (error);
}
if (vp->v_type == VFIFO && VOP_ISLOCKED(vp) != LK_EXCLUSIVE)
vn_lock(vp, LK_UPGRADE | LK_RETRY);
- if ((error = VOP_OPEN(vp, fmode, cred, td, fp)) != 0)
+ error = VOP_OPEN(vp, fmode, cred, td, fp);
+ if (error != 0)
return (error);
- while ((fmode & (O_EXLOCK | O_SHLOCK)) != 0) {
- KASSERT(fp != NULL, ("open with flock requires fp"));
- if (fp->f_type != DTYPE_NONE && fp->f_type != DTYPE_VNODE) {
- error = EOPNOTSUPP;
- break;
- }
- 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);
- if (error == 0)
- fp->f_flag |= FHASLOCK;
- vn_lock(vp, lock_flags | LK_RETRY);
- if (error != 0)
- break;
- if ((vp->v_iflag & VI_DOOMED) != 0) {
- error = ENOENT;
- break;
+ error = vn_open_vnode_advlock(vp, fmode, fp);
+ if (error == 0 && (fmode & FWRITE) != 0) {
+ error = VOP_ADD_WRITECOUNT(vp, 1);
+ if (error == 0) {
+ CTR3(KTR_VFS, "%s: vp %p v_writecount increased to %d",
+ __func__, vp, vp->v_writecount);
}
-
- /*
- * 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 ((accmode & VWRITE) != 0)
- error = vn_writechk(vp);
- break;
}
+ /*
+ * Error from advlock or VOP_ADD_WRITECOUNT() still requires
+ * calling VOP_CLOSE() to pair with earlier VOP_OPEN().
+ * Arrange for that by having fdrop() to use vn_closefile().
+ */
if (error != 0) {
fp->f_flag |= FOPENFAILED;
fp->f_vnode = vp;
@@ -400,18 +400,17 @@ vn_open_vnode(struct vnode *vp, int fmode, struct ucred *cred,
fp->f_ops = &vnops;
}
vref(vp);
- } else if ((fmode & FWRITE) != 0) {
- VOP_ADD_WRITECOUNT(vp, 1);
- CTR3(KTR_VFS, "%s: vp %p v_writecount increased to %d",
- __func__, vp, vp->v_writecount);
}
+
ASSERT_VOP_LOCKED(vp, "vn_open_vnode");
return (error);
+
}
/*
* Check for write permissions on the specified vnode.
* Prototype text segments cannot be written.
+ * It is racy.
*/
int
vn_writechk(struct vnode *vp)
@@ -449,9 +448,7 @@ vn_close1(struct vnode *vp, int flags, struct ucred *file_cred,
vn_lock(vp, lock_flags | LK_RETRY);
AUDIT_ARG_VNODE1(vp);
if ((flags & (FWRITE | FOPENFAILED)) == FWRITE) {
- VNASSERT(vp->v_writecount > 0, vp,
- ("vn_close: negative writecount"));
- VOP_ADD_WRITECOUNT(vp, -1);
+ VOP_ADD_WRITECOUNT_CHECKED(vp, -1);
CTR3(KTR_VFS, "%s: vp %p v_writecount decreased to %d",
__func__, vp, vp->v_writecount);
}
@@ -1319,13 +1316,14 @@ vn_truncate(struct file *fp, off_t length, struct ucred *active_cred,
if (error)
goto out;
#endif
- error = vn_writechk(vp);
+ error = VOP_ADD_WRITECOUNT(vp, 1);
if (error == 0) {
VATTR_NULL(&vattr);
vattr.va_size = length;
if ((fp->f_flag & O_FSYNC) != 0)
vattr.va_vaflags |= VA_SYNC;
error = VOP_SETATTR(vp, &vattr, fp->f_cred);
+ VOP_ADD_WRITECOUNT_CHECKED(vp, -1);
}
out:
VOP_UNLOCK(vp, 0);
diff --git a/sys/kern/vnode_if.src b/sys/kern/vnode_if.src
index 5b21026b517b..68fa84079af6 100644
--- a/sys/kern/vnode_if.src
+++ b/sys/kern/vnode_if.src
@@ -688,29 +688,21 @@ vop_is_text {
};
-%% set_text vp E E E
+%% set_text vp = = =
vop_set_text {
IN struct vnode *vp;
};
-%% vop_unset_text vp E E E
+%% vop_unset_text vp = = =
vop_unset_text {
IN struct vnode *vp;
};
-%% get_writecount vp L L L
-
-vop_get_writecount {
- IN struct vnode *vp;
- OUT int *writecount;
-};
-
-
-%% add_writecount vp E E E
+%% add_writecount vp L L L
vop_add_writecount {
IN struct vnode *vp;