summaryrefslogtreecommitdiffstats
path: root/sys/ufs/ffs
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2009-03-12 12:43:56 +0000
committerkib <kib@FreeBSD.org>2009-03-12 12:43:56 +0000
commit9e27cf574d2cb8f717bf50b309f68de1fac30375 (patch)
treecf17c989527b9b03232ce80b2268236e8559acb0 /sys/ufs/ffs
parent33b0309eb2b6e212d5a03522483eb96411ab631a (diff)
downloadFreeBSD-src-9e27cf574d2cb8f717bf50b309f68de1fac30375.zip
FreeBSD-src-9e27cf574d2cb8f717bf50b309f68de1fac30375.tar.gz
The non-modifying EA VOPs are executed with only shared vnode lock taken.
Provide a custom lock around initializing and tearing down EA area, to prevent both memory leaks and double-free of it. Count the number of EA area accessors. Lock protocol requires either holding exclusive vnode lock to modify i_ea_area, or shared vnode lock and owning IN_EA_LOCKED flag in i_flag. Noted by: YAMAMOTO, Taku <taku tackymt homeip net> Tested by: pho (previous version) MFC after: 2 weeks
Diffstat (limited to 'sys/ufs/ffs')
-rw-r--r--sys/ufs/ffs/ffs_vfsops.c1
-rw-r--r--sys/ufs/ffs/ffs_vnops.c153
2 files changed, 91 insertions, 63 deletions
diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c
index 73535fc..6d41d6f 100644
--- a/sys/ufs/ffs/ffs_vfsops.c
+++ b/sys/ufs/ffs/ffs_vfsops.c
@@ -1451,6 +1451,7 @@ ffs_vgetf(mp, ino, flags, vpp, ffs_flags)
ip->i_fs = fs;
ip->i_dev = dev;
ip->i_number = ino;
+ ip->i_ea_refs = 0;
#ifdef QUOTA
{
int i;
diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c
index ca2efa6..161a14d 100644
--- a/sys/ufs/ffs/ffs_vnops.c
+++ b/sys/ufs/ffs/ffs_vnops.c
@@ -1225,6 +1225,35 @@ ffs_rdextattr(u_char **p, struct vnode *vp, struct thread *td, int extra)
return (0);
}
+static void
+ffs_lock_ea(struct vnode *vp)
+{
+ struct inode *ip;
+
+ ip = VTOI(vp);
+ VI_LOCK(vp);
+ while (ip->i_flag & IN_EA_LOCKED) {
+ ip->i_flag |= IN_EA_LOCKWAIT;
+ msleep(&ip->i_ea_refs, &vp->v_interlock, PINOD + 2, "ufs_ea",
+ 0);
+ }
+ ip->i_flag |= IN_EA_LOCKED;
+ VI_UNLOCK(vp);
+}
+
+static void
+ffs_unlock_ea(struct vnode *vp)
+{
+ struct inode *ip;
+
+ ip = VTOI(vp);
+ VI_LOCK(vp);
+ if (ip->i_flag & IN_EA_LOCKWAIT)
+ wakeup(&ip->i_ea_refs);
+ ip->i_flag &= ~(IN_EA_LOCKED | IN_EA_LOCKWAIT);
+ VI_UNLOCK(vp);
+}
+
static int
ffs_open_ea(struct vnode *vp, struct ucred *cred, struct thread *td)
{
@@ -1234,14 +1263,22 @@ ffs_open_ea(struct vnode *vp, struct ucred *cred, struct thread *td)
ip = VTOI(vp);
- if (ip->i_ea_area != NULL)
- return (EBUSY);
+ ffs_lock_ea(vp);
+ if (ip->i_ea_area != NULL) {
+ ip->i_ea_refs++;
+ ffs_unlock_ea(vp);
+ return (0);
+ }
dp = ip->i_din2;
error = ffs_rdextattr(&ip->i_ea_area, vp, td, 0);
- if (error)
+ if (error) {
+ ffs_unlock_ea(vp);
return (error);
+ }
ip->i_ea_len = dp->di_extsize;
ip->i_ea_error = 0;
+ ip->i_ea_refs++;
+ ffs_unlock_ea(vp);
return (0);
}
@@ -1258,11 +1295,16 @@ ffs_close_ea(struct vnode *vp, int commit, struct ucred *cred, struct thread *td
struct ufs2_dinode *dp;
ip = VTOI(vp);
- if (ip->i_ea_area == NULL)
+
+ ffs_lock_ea(vp);
+ if (ip->i_ea_area == NULL) {
+ ffs_unlock_ea(vp);
return (EINVAL);
+ }
dp = ip->i_din2;
error = ip->i_ea_error;
if (commit && error == 0) {
+ ASSERT_VOP_ELOCKED(vp, "ffs_close_ea commit");
if (cred == NOCRED)
cred = vp->v_mount->mnt_cred;
liovec.iov_base = ip->i_ea_area;
@@ -1279,10 +1321,13 @@ ffs_close_ea(struct vnode *vp, int commit, struct ucred *cred, struct thread *td
error = ffs_truncate(vp, 0, IO_EXT, cred, td);
error = ffs_extwrite(vp, &luio, IO_EXT | IO_SYNC, cred);
}
- free(ip->i_ea_area, M_TEMP);
- ip->i_ea_area = NULL;
- ip->i_ea_len = 0;
- ip->i_ea_error = 0;
+ if (--ip->i_ea_refs == 0) {
+ free(ip->i_ea_area, M_TEMP);
+ ip->i_ea_area = NULL;
+ ip->i_ea_len = 0;
+ ip->i_ea_error = 0;
+ }
+ ffs_unlock_ea(vp);
return (error);
}
@@ -1392,7 +1437,6 @@ vop_deleteextattr {
uint32_t ealength, ul;
int ealen, olen, eapad1, eapad2, error, i, easize;
u_char *eae, *p;
- int stand_alone;
ip = VTOI(ap->a_vp);
fs = ip->i_fs;
@@ -1409,19 +1453,19 @@ vop_deleteextattr {
error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace,
ap->a_cred, ap->a_td, VWRITE);
if (error) {
+
+ /*
+ * ffs_lock_ea is not needed there, because the vnode
+ * must be exlusively locked.
+ */
if (ip->i_ea_area != NULL && ip->i_ea_error == 0)
ip->i_ea_error = error;
return (error);
}
- if (ip->i_ea_area == NULL) {
- error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
- if (error)
- return (error);
- stand_alone = 1;
- } else {
- stand_alone = 0;
- }
+ error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
+ if (error)
+ return (error);
ealength = eapad1 = ealen = eapad2 = 0;
@@ -1434,8 +1478,7 @@ vop_deleteextattr {
if (olen == -1) {
/* delete but nonexistent */
free(eae, M_TEMP);
- if (stand_alone)
- ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
+ ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
return(ENOATTR);
}
bcopy(p, &ul, sizeof ul);
@@ -1446,9 +1489,8 @@ vop_deleteextattr {
}
if (easize > NXADDR * fs->fs_bsize) {
free(eae, M_TEMP);
- if (stand_alone)
- ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
- else if (ip->i_ea_error == 0)
+ ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
+ if (ip->i_ea_area != NULL && ip->i_ea_error == 0)
ip->i_ea_error = ENOSPC;
return(ENOSPC);
}
@@ -1456,8 +1498,7 @@ vop_deleteextattr {
ip->i_ea_area = eae;
ip->i_ea_len = easize;
free(p, M_TEMP);
- if (stand_alone)
- error = ffs_close_ea(ap->a_vp, 1, ap->a_cred, ap->a_td);
+ error = ffs_close_ea(ap->a_vp, 1, ap->a_cred, ap->a_td);
return(error);
}
@@ -1482,7 +1523,7 @@ vop_getextattr {
struct fs *fs;
u_char *eae, *p;
unsigned easize;
- int error, ealen, stand_alone;
+ int error, ealen;
ip = VTOI(ap->a_vp);
fs = ip->i_fs;
@@ -1495,14 +1536,10 @@ vop_getextattr {
if (error)
return (error);
- if (ip->i_ea_area == NULL) {
- error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
- if (error)
- return (error);
- stand_alone = 1;
- } else {
- stand_alone = 0;
- }
+ error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
+ if (error)
+ return (error);
+
eae = ip->i_ea_area;
easize = ip->i_ea_len;
@@ -1516,8 +1553,8 @@ vop_getextattr {
error = uiomove(p, ealen, ap->a_uio);
} else
error = ENOATTR;
- if (stand_alone)
- ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
+
+ ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
return(error);
}
@@ -1542,7 +1579,7 @@ vop_listextattr {
u_char *eae, *p, *pe, *pn;
unsigned easize;
uint32_t ul;
- int error, ealen, stand_alone;
+ int error, ealen;
ip = VTOI(ap->a_vp);
fs = ip->i_fs;
@@ -1555,14 +1592,9 @@ vop_listextattr {
if (error)
return (error);
- if (ip->i_ea_area == NULL) {
- error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
- if (error)
- return (error);
- stand_alone = 1;
- } else {
- stand_alone = 0;
- }
+ error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
+ if (error)
+ return (error);
eae = ip->i_ea_area;
easize = ip->i_ea_len;
@@ -1586,8 +1618,7 @@ vop_listextattr {
error = uiomove(p, ealen + 1, ap->a_uio);
}
}
- if (stand_alone)
- ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
+ ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
return(error);
}
@@ -1612,7 +1643,6 @@ vop_setextattr {
uint32_t ealength, ul;
int ealen, olen, eapad1, eapad2, error, i, easize;
u_char *eae, *p;
- int stand_alone;
ip = VTOI(ap->a_vp);
fs = ip->i_fs;
@@ -1633,19 +1663,19 @@ vop_setextattr {
error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace,
ap->a_cred, ap->a_td, VWRITE);
if (error) {
+
+ /*
+ * ffs_lock_ea is not needed there, because the vnode
+ * must be exlusively locked.
+ */
if (ip->i_ea_area != NULL && ip->i_ea_error == 0)
ip->i_ea_error = error;
return (error);
}
- if (ip->i_ea_area == NULL) {
- error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
- if (error)
- return (error);
- stand_alone = 1;
- } else {
- stand_alone = 0;
- }
+ error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td);
+ if (error)
+ return (error);
ealen = ap->a_uio->uio_resid;
ealength = sizeof(uint32_t) + 3 + strlen(ap->a_name);
@@ -1677,9 +1707,8 @@ vop_setextattr {
}
if (easize > NXADDR * fs->fs_bsize) {
free(eae, M_TEMP);
- if (stand_alone)
- ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
- else if (ip->i_ea_error == 0)
+ ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
+ if (ip->i_ea_area != NULL && ip->i_ea_error == 0)
ip->i_ea_error = ENOSPC;
return(ENOSPC);
}
@@ -1695,9 +1724,8 @@ vop_setextattr {
error = uiomove(p, ealen, ap->a_uio);
if (error) {
free(eae, M_TEMP);
- if (stand_alone)
- ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
- else if (ip->i_ea_error == 0)
+ ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td);
+ if (ip->i_ea_area != NULL && ip->i_ea_error == 0)
ip->i_ea_error = error;
return(error);
}
@@ -1708,8 +1736,7 @@ vop_setextattr {
ip->i_ea_area = eae;
ip->i_ea_len = easize;
free(p, M_TEMP);
- if (stand_alone)
- error = ffs_close_ea(ap->a_vp, 1, ap->a_cred, ap->a_td);
+ error = ffs_close_ea(ap->a_vp, 1, ap->a_cred, ap->a_td);
return(error);
}
OpenPOWER on IntegriCloud