summaryrefslogtreecommitdiff
path: root/drivers/staging/erofs
diff options
context:
space:
mode:
authorGao Xiang <gaoxiang25@huawei.com>2019-02-18 10:19:04 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-02-20 13:20:55 +0300
commit62dc45979f3f8cb0ea67302a93bff686f0c46c5a (patch)
tree51eacdc5461cdf651eab37a0277e96e08e0bff0a /drivers/staging/erofs
parent17b7abbf1e3bf143027352a5da38611c9c4e8c96 (diff)
downloadlinux-62dc45979f3f8cb0ea67302a93bff686f0c46c5a.tar.xz
staging: erofs: fix race of initializing xattrs of a inode at the same time
In real scenario, there could be several threads accessing xattrs of the same xattr-uninitialized inode, and init_inode_xattrs() almost at the same time. That's actually an unexpected behavior, this patch closes the race. Fixes: b17500a0fdba ("staging: erofs: introduce xattr & acl support") Cc: <stable@vger.kernel.org> # 4.19+ Reviewed-by: Chao Yu <yuchao0@huawei.com> Signed-off-by: Gao Xiang <gaoxiang25@huawei.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/staging/erofs')
-rw-r--r--drivers/staging/erofs/internal.h11
-rw-r--r--drivers/staging/erofs/xattr.c41
2 files changed, 37 insertions, 15 deletions
diff --git a/drivers/staging/erofs/internal.h b/drivers/staging/erofs/internal.h
index 3a27c255950b..e3bfde00c7d2 100644
--- a/drivers/staging/erofs/internal.h
+++ b/drivers/staging/erofs/internal.h
@@ -327,12 +327,17 @@ static inline erofs_off_t iloc(struct erofs_sb_info *sbi, erofs_nid_t nid)
return blknr_to_addr(sbi->meta_blkaddr) + (nid << sbi->islotbits);
}
-#define inode_set_inited_xattr(inode) (EROFS_V(inode)->flags |= 1)
-#define inode_has_inited_xattr(inode) (EROFS_V(inode)->flags & 1)
+/* atomic flag definitions */
+#define EROFS_V_EA_INITED_BIT 0
+
+/* bitlock definitions (arranged in reverse order) */
+#define EROFS_V_BL_XATTR_BIT (BITS_PER_LONG - 1)
struct erofs_vnode {
erofs_nid_t nid;
- unsigned int flags;
+
+ /* atomic flags (including bitlocks) */
+ unsigned long flags;
unsigned char data_mapping_mode;
/* inline size in bytes */
diff --git a/drivers/staging/erofs/xattr.c b/drivers/staging/erofs/xattr.c
index 8c48b0ab31fd..f716ab0446e5 100644
--- a/drivers/staging/erofs/xattr.c
+++ b/drivers/staging/erofs/xattr.c
@@ -44,18 +44,25 @@ static inline void xattr_iter_end_final(struct xattr_iter *it)
static int init_inode_xattrs(struct inode *inode)
{
+ struct erofs_vnode *const vi = EROFS_V(inode);
struct xattr_iter it;
unsigned int i;
struct erofs_xattr_ibody_header *ih;
struct super_block *sb;
struct erofs_sb_info *sbi;
- struct erofs_vnode *vi;
bool atomic_map;
+ int ret = 0;
- if (likely(inode_has_inited_xattr(inode)))
+ /* the most case is that xattrs of this inode are initialized. */
+ if (test_bit(EROFS_V_EA_INITED_BIT, &vi->flags))
return 0;
- vi = EROFS_V(inode);
+ if (wait_on_bit_lock(&vi->flags, EROFS_V_BL_XATTR_BIT, TASK_KILLABLE))
+ return -ERESTARTSYS;
+
+ /* someone has initialized xattrs for us? */
+ if (test_bit(EROFS_V_EA_INITED_BIT, &vi->flags))
+ goto out_unlock;
/*
* bypass all xattr operations if ->xattr_isize is not greater than
@@ -68,13 +75,16 @@ static int init_inode_xattrs(struct inode *inode)
if (vi->xattr_isize == sizeof(struct erofs_xattr_ibody_header)) {
errln("xattr_isize %d of nid %llu is not supported yet",
vi->xattr_isize, vi->nid);
- return -ENOTSUPP;
+ ret = -ENOTSUPP;
+ goto out_unlock;
} else if (vi->xattr_isize < sizeof(struct erofs_xattr_ibody_header)) {
if (unlikely(vi->xattr_isize)) {
DBG_BUGON(1);
- return -EIO; /* xattr ondisk layout error */
+ ret = -EIO;
+ goto out_unlock; /* xattr ondisk layout error */
}
- return -ENOATTR;
+ ret = -ENOATTR;
+ goto out_unlock;
}
sb = inode->i_sb;
@@ -83,8 +93,10 @@ static int init_inode_xattrs(struct inode *inode)
it.ofs = erofs_blkoff(iloc(sbi, vi->nid) + vi->inode_isize);
it.page = erofs_get_inline_page(inode, it.blkaddr);
- if (IS_ERR(it.page))
- return PTR_ERR(it.page);
+ if (IS_ERR(it.page)) {
+ ret = PTR_ERR(it.page);
+ goto out_unlock;
+ }
/* read in shared xattr array (non-atomic, see kmalloc below) */
it.kaddr = kmap(it.page);
@@ -97,7 +109,8 @@ static int init_inode_xattrs(struct inode *inode)
sizeof(uint), GFP_KERNEL);
if (vi->xattr_shared_xattrs == NULL) {
xattr_iter_end(&it, atomic_map);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto out_unlock;
}
/* let's skip ibody header */
@@ -114,7 +127,8 @@ static int init_inode_xattrs(struct inode *inode)
if (IS_ERR(it.page)) {
kfree(vi->xattr_shared_xattrs);
vi->xattr_shared_xattrs = NULL;
- return PTR_ERR(it.page);
+ ret = PTR_ERR(it.page);
+ goto out_unlock;
}
it.kaddr = kmap_atomic(it.page);
@@ -127,8 +141,11 @@ static int init_inode_xattrs(struct inode *inode)
}
xattr_iter_end(&it, atomic_map);
- inode_set_inited_xattr(inode);
- return 0;
+ set_bit(EROFS_V_EA_INITED_BIT, &vi->flags);
+
+out_unlock:
+ clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags);
+ return ret;
}
/*