Commit 62dc4597 authored by Gao Xiang's avatar Gao Xiang Committed by Greg Kroah-Hartman

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: b17500a0 ("staging: erofs: introduce xattr & acl support")
Cc: <stable@vger.kernel.org> # 4.19+
Reviewed-by: default avatarChao Yu <yuchao0@huawei.com>
Signed-off-by: default avatarGao Xiang <gaoxiang25@huawei.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 17b7abbf
...@@ -327,12 +327,17 @@ static inline erofs_off_t iloc(struct erofs_sb_info *sbi, erofs_nid_t nid) ...@@ -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); return blknr_to_addr(sbi->meta_blkaddr) + (nid << sbi->islotbits);
} }
#define inode_set_inited_xattr(inode) (EROFS_V(inode)->flags |= 1) /* atomic flag definitions */
#define inode_has_inited_xattr(inode) (EROFS_V(inode)->flags & 1) #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 { struct erofs_vnode {
erofs_nid_t nid; erofs_nid_t nid;
unsigned int flags;
/* atomic flags (including bitlocks) */
unsigned long flags;
unsigned char data_mapping_mode; unsigned char data_mapping_mode;
/* inline size in bytes */ /* inline size in bytes */
......
...@@ -44,18 +44,25 @@ static inline void xattr_iter_end_final(struct xattr_iter *it) ...@@ -44,18 +44,25 @@ static inline void xattr_iter_end_final(struct xattr_iter *it)
static int init_inode_xattrs(struct inode *inode) static int init_inode_xattrs(struct inode *inode)
{ {
struct erofs_vnode *const vi = EROFS_V(inode);
struct xattr_iter it; struct xattr_iter it;
unsigned int i; unsigned int i;
struct erofs_xattr_ibody_header *ih; struct erofs_xattr_ibody_header *ih;
struct super_block *sb; struct super_block *sb;
struct erofs_sb_info *sbi; struct erofs_sb_info *sbi;
struct erofs_vnode *vi;
bool atomic_map; 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; 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 * bypass all xattr operations if ->xattr_isize is not greater than
...@@ -68,13 +75,16 @@ static int init_inode_xattrs(struct inode *inode) ...@@ -68,13 +75,16 @@ static int init_inode_xattrs(struct inode *inode)
if (vi->xattr_isize == sizeof(struct erofs_xattr_ibody_header)) { if (vi->xattr_isize == sizeof(struct erofs_xattr_ibody_header)) {
errln("xattr_isize %d of nid %llu is not supported yet", errln("xattr_isize %d of nid %llu is not supported yet",
vi->xattr_isize, vi->nid); vi->xattr_isize, vi->nid);
return -ENOTSUPP; ret = -ENOTSUPP;
goto out_unlock;
} else if (vi->xattr_isize < sizeof(struct erofs_xattr_ibody_header)) { } else if (vi->xattr_isize < sizeof(struct erofs_xattr_ibody_header)) {
if (unlikely(vi->xattr_isize)) { if (unlikely(vi->xattr_isize)) {
DBG_BUGON(1); 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; sb = inode->i_sb;
...@@ -83,8 +93,10 @@ static int init_inode_xattrs(struct inode *inode) ...@@ -83,8 +93,10 @@ static int init_inode_xattrs(struct inode *inode)
it.ofs = erofs_blkoff(iloc(sbi, vi->nid) + vi->inode_isize); it.ofs = erofs_blkoff(iloc(sbi, vi->nid) + vi->inode_isize);
it.page = erofs_get_inline_page(inode, it.blkaddr); it.page = erofs_get_inline_page(inode, it.blkaddr);
if (IS_ERR(it.page)) if (IS_ERR(it.page)) {
return PTR_ERR(it.page); ret = PTR_ERR(it.page);
goto out_unlock;
}
/* read in shared xattr array (non-atomic, see kmalloc below) */ /* read in shared xattr array (non-atomic, see kmalloc below) */
it.kaddr = kmap(it.page); it.kaddr = kmap(it.page);
...@@ -97,7 +109,8 @@ static int init_inode_xattrs(struct inode *inode) ...@@ -97,7 +109,8 @@ static int init_inode_xattrs(struct inode *inode)
sizeof(uint), GFP_KERNEL); sizeof(uint), GFP_KERNEL);
if (vi->xattr_shared_xattrs == NULL) { if (vi->xattr_shared_xattrs == NULL) {
xattr_iter_end(&it, atomic_map); xattr_iter_end(&it, atomic_map);
return -ENOMEM; ret = -ENOMEM;
goto out_unlock;
} }
/* let's skip ibody header */ /* let's skip ibody header */
...@@ -114,7 +127,8 @@ static int init_inode_xattrs(struct inode *inode) ...@@ -114,7 +127,8 @@ static int init_inode_xattrs(struct inode *inode)
if (IS_ERR(it.page)) { if (IS_ERR(it.page)) {
kfree(vi->xattr_shared_xattrs); kfree(vi->xattr_shared_xattrs);
vi->xattr_shared_xattrs = NULL; vi->xattr_shared_xattrs = NULL;
return PTR_ERR(it.page); ret = PTR_ERR(it.page);
goto out_unlock;
} }
it.kaddr = kmap_atomic(it.page); it.kaddr = kmap_atomic(it.page);
...@@ -127,8 +141,11 @@ static int init_inode_xattrs(struct inode *inode) ...@@ -127,8 +141,11 @@ static int init_inode_xattrs(struct inode *inode)
} }
xattr_iter_end(&it, atomic_map); xattr_iter_end(&it, atomic_map);
inode_set_inited_xattr(inode); set_bit(EROFS_V_EA_INITED_BIT, &vi->flags);
return 0;
out_unlock:
clear_and_wake_up_bit(EROFS_V_BL_XATTR_BIT, &vi->flags);
return ret;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment