diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c index 73535fc..379c173 100644 --- a/sys/ufs/ffs/ffs_vfsops.c +++ b/sys/ufs/ffs/ffs_vfsops.c @@ -1451,6 +1451,8 @@ ffs_vgetf(mp, ino, flags, vpp, ffs_flags) ip->i_fs = fs; ip->i_dev = dev; ip->i_number = ino; + sx_init(&ip->i_ea_lock, "UFS_EA"); + ip->i_ea_refs = 0; #ifdef QUOTA { int i; @@ -1717,6 +1719,7 @@ ffs_ifree(struct ufsmount *ump, struct inode *ip) uma_zfree(uma_ufs1, ip->i_din1); else if (ip->i_din2 != NULL) uma_zfree(uma_ufs2, ip->i_din2); + sx_destroy(&ip->i_ea_lock); uma_zfree(uma_inode, ip); } diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c index ca2efa6..0fd352f 100644 --- a/sys/ufs/ffs/ffs_vnops.c +++ b/sys/ufs/ffs/ffs_vnops.c @@ -1234,14 +1234,21 @@ ffs_open_ea(struct vnode *vp, struct ucred *cred, struct thread *td) ip = VTOI(vp); - if (ip->i_ea_area != NULL) - return (EBUSY); + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE || + (VOP_ISLOCKED(vp) == LK_SHARED && sx_xlocked(&ip->i_ea_lock)), + ("ffs ea")); + + if (ip->i_ea_area != NULL) { + ip->i_ea_refs++; + return (0); + } dp = ip->i_din2; error = ffs_rdextattr(&ip->i_ea_area, vp, td, 0); if (error) return (error); ip->i_ea_len = dp->di_extsize; ip->i_ea_error = 0; + ip->i_ea_refs++; return (0); } @@ -1258,11 +1265,17 @@ ffs_close_ea(struct vnode *vp, int commit, struct ucred *cred, struct thread *td struct ufs2_dinode *dp; ip = VTOI(vp); + + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE || + (VOP_ISLOCKED(vp) == LK_SHARED && sx_xlocked(&ip->i_ea_lock)), + ("ffs ea")); + if (ip->i_ea_area == NULL) 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 +1292,12 @@ 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; + } return (error); } @@ -1315,6 +1330,9 @@ struct vop_strategy_args { panic("spec nodes went here"); } +#define UFS_LOCK_EA(ip) sx_xlock(&(ip)->i_ea_lock) +#define UFS_UNLOCK_EA(ip) sx_xunlock(&(ip)->i_ea_lock) + /* * Vnode extattr transaction commit/abort */ @@ -1331,6 +1349,7 @@ struct vop_openextattr_args { { struct inode *ip; struct fs *fs; + int error; ip = VTOI(ap->a_vp); fs = ip->i_fs; @@ -1338,7 +1357,10 @@ struct vop_openextattr_args { if (ap->a_vp->v_type == VCHR) return (EOPNOTSUPP); - return (ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td)); + UFS_LOCK_EA(ip); + error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td); + UFS_UNLOCK_EA(ip); + return (error); } @@ -1359,6 +1381,7 @@ struct vop_closeextattr_args { { struct inode *ip; struct fs *fs; + int error; ip = VTOI(ap->a_vp); fs = ip->i_fs; @@ -1369,7 +1392,10 @@ struct vop_closeextattr_args { if (ap->a_commit && (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)) return (EROFS); - return (ffs_close_ea(ap->a_vp, ap->a_commit, ap->a_cred, ap->a_td)); + UFS_LOCK_EA(ip); + error = ffs_close_ea(ap->a_vp, ap->a_commit, ap->a_cred, ap->a_td); + UFS_UNLOCK_EA(ip); + return (error); } /* @@ -1392,7 +1418,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 +1434,19 @@ vop_deleteextattr { error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, ap->a_td, VWRITE); if (error) { + + /* + * UFS_LOCK_EA is not needed, because 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 +1459,9 @@ 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); + UFS_LOCK_EA(ip); + ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td); + UFS_UNLOCK_EA(ip); return(ENOATTR); } bcopy(p, &ul, sizeof ul); @@ -1446,9 +1472,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_error == 0) ip->i_ea_error = ENOSPC; return(ENOSPC); } @@ -1456,8 +1481,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 +1506,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 +1519,12 @@ 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; - } + UFS_LOCK_EA(ip); + error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td); + UFS_UNLOCK_EA(ip); + if (error) + return (error); + eae = ip->i_ea_area; easize = ip->i_ea_len; @@ -1516,8 +1538,10 @@ 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); + + UFS_LOCK_EA(ip); + ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td); + UFS_UNLOCK_EA(ip); return(error); } @@ -1542,7 +1566,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 +1579,11 @@ 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; - } + UFS_LOCK_EA(ip); + error = ffs_open_ea(ap->a_vp, ap->a_cred, ap->a_td); + UFS_UNLOCK_EA(ip); + if (error) + return (error); eae = ip->i_ea_area; easize = ip->i_ea_len; @@ -1586,8 +1607,9 @@ 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); + UFS_LOCK_EA(ip); + ffs_close_ea(ap->a_vp, 0, ap->a_cred, ap->a_td); + UFS_UNLOCK_EA(ip); return(error); } @@ -1612,7 +1634,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; @@ -1638,14 +1659,9 @@ vop_setextattr { 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 +1693,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 +1710,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 +1722,9 @@ 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); + UFS_LOCK_EA(ip); + error = ffs_close_ea(ap->a_vp, 1, ap->a_cred, ap->a_td); + UFS_UNLOCK_EA(ip); return(error); } diff --git a/sys/ufs/ufs/inode.h b/sys/ufs/ufs/inode.h index 39f46d0..470a9d8 100644 --- a/sys/ufs/ufs/inode.h +++ b/sys/ufs/ufs/inode.h @@ -39,6 +39,7 @@ #define _UFS_UFS_INODE_H_ #include +#include #include #include @@ -94,6 +95,8 @@ struct inode { u_char *i_ea_area; /* Pointer to malloced copy of EA area */ unsigned i_ea_len; /* Length of i_ea_area */ int i_ea_error; /* First errno in transaction */ + int i_ea_refs; /* Number of users of the ea area */ + struct sx i_ea_lock; /* Protects ea area instantiation and teardown */ /* * Copies from the on-disk dinode itself.