aboutsummaryrefslogtreecommitdiff
path: root/sys/kern
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/vfs_default.c15
-rw-r--r--sys/kern/vfs_vnops.c23
2 files changed, 35 insertions, 3 deletions
diff --git a/sys/kern/vfs_default.c b/sys/kern/vfs_default.c
index 382fbb2d9ace..3c428d7b7511 100644
--- a/sys/kern/vfs_default.c
+++ b/sys/kern/vfs_default.c
@@ -423,10 +423,25 @@ int
vop_stdadvlock(struct vop_advlock_args *ap)
{
struct vnode *vp;
+ struct mount *mp;
struct vattr vattr;
int error;
vp = ap->a_vp;
+
+ /*
+ * Provide atomicity of open(O_CREAT | O_EXCL | O_EXLOCK) for
+ * local filesystems. See vn_open_cred() for reciprocal part.
+ */
+ mp = vp->v_mount;
+ if (mp != NULL && (mp->mnt_flag & MNT_LOCAL) != 0 &&
+ ap->a_op == F_SETLK && (ap->a_flags & F_FIRSTOPEN) == 0) {
+ VI_LOCK(vp);
+ while ((vp->v_iflag & VI_FOPENING) != 0)
+ msleep(vp, VI_MTX(vp), PLOCK, "lockfo", 0);
+ VI_UNLOCK(vp);
+ }
+
if (ap->a_fl->l_whence == SEEK_END) {
/*
* The NFSv4 server must avoid doing a vn_lock() here, since it
diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c
index 71dd379558cb..781968f2db53 100644
--- a/sys/kern/vfs_vnops.c
+++ b/sys/kern/vfs_vnops.c
@@ -228,8 +228,10 @@ vn_open_cred(struct nameidata *ndp, int *flagp, int cmode, u_int vn_open_flags,
struct vattr vat;
struct vattr *vap = &vat;
int fmode, error;
+ bool first_open;
restart:
+ first_open = false;
fmode = *flagp;
if ((fmode & (O_CREAT | O_EXCL | O_DIRECTORY)) == (O_CREAT |
O_EXCL | O_DIRECTORY))
@@ -275,8 +277,16 @@ restart:
#endif
error = VOP_CREATE(ndp->ni_dvp, &ndp->ni_vp,
&ndp->ni_cnd, vap);
- VOP_VPUT_PAIR(ndp->ni_dvp, error == 0 ? &ndp->ni_vp :
- NULL, false);
+ vp = ndp->ni_vp;
+ if (error == 0 && (fmode & O_EXCL) != 0 &&
+ (fmode & (O_EXLOCK | O_SHLOCK)) != 0) {
+ VI_LOCK(vp);
+ vp->v_iflag |= VI_FOPENING;
+ VI_UNLOCK(vp);
+ first_open = true;
+ }
+ VOP_VPUT_PAIR(ndp->ni_dvp, error == 0 ? &vp : NULL,
+ false);
vn_finished_write(mp);
if (error) {
NDFREE(ndp, NDF_ONLY_PNBUF);
@@ -287,7 +297,6 @@ restart:
return (error);
}
fmode &= ~O_TRUNC;
- vp = ndp->ni_vp;
} else {
if (ndp->ni_dvp == ndp->ni_vp)
vrele(ndp->ni_dvp);
@@ -317,6 +326,12 @@ restart:
vp = ndp->ni_vp;
}
error = vn_open_vnode(vp, fmode, cred, td, fp);
+ if (first_open) {
+ VI_LOCK(vp);
+ vp->v_iflag &= ~VI_FOPENING;
+ wakeup(vp);
+ VI_UNLOCK(vp);
+ }
if (error)
goto bad;
*flagp = fmode;
@@ -352,6 +367,8 @@ vn_open_vnode_advlock(struct vnode *vp, int fmode, struct file *fp)
type = F_FLOCK;
if ((fmode & FNONBLOCK) == 0)
type |= F_WAIT;
+ if ((fmode & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ type |= F_FIRSTOPEN;
error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type);
if (error == 0)
fp->f_flag |= FHASLOCK;