diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index bb5b12924e1b..d6d07116ce97 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -6052,13 +6052,18 @@ static int ocfs2_xattr_set_entry_bucket(struct inode *inode, struct ocfs2_xattr_search *xs, struct ocfs2_xattr_set_ctxt *ctxt) { - int ret; + int ret, found = 0; + u16 xe_index; + u32 name_hash; struct ocfs2_xa_loc loc; trace_ocfs2_xattr_set_entry_bucket(xi->xi_name); ocfs2_init_xattr_bucket_xa_loc(&loc, xs->bucket, xs->not_found ? NULL : xs->here); + /* Defragmenting below may move this entry, so remember its hash. */ + name_hash = loc.xl_entry ? le32_to_cpu(loc.xl_entry->xe_name_hash) : 0; + ret = ocfs2_xa_set(&loc, xi, ctxt); if (!ret) { xs->here = loc.xl_entry; @@ -6077,6 +6082,29 @@ static int ocfs2_xattr_set_entry_bucket(struct inode *inode, goto out; } + /* + * ocfs2_defrag_xattr_bucket() sorts the entries by name hash and + * lib/sort.c is not stable, so entries sharing a hash may have traded + * slots. Look the entry up again before writing through it. + */ + if (loc.xl_entry) { + ret = ocfs2_find_xe_in_bucket(inode, xs->bucket, + xi->xi_name_index, xi->xi_name, + name_hash, &xe_index, &found); + if (ret) { + mlog_errno(ret); + goto out; + } + if (!found) { + mlog(ML_ERROR, "xattr entry not found in bucket %llu after defrag\n", + (unsigned long long)bucket_blkno(xs->bucket)); + ret = -EFSCORRUPTED; + goto out; + } + loc.xl_entry = &bucket_xh(xs->bucket)->xh_entries[xe_index]; + xs->here = loc.xl_entry; + } + ret = ocfs2_xa_set(&loc, xi, ctxt); if (!ret) { xs->here = loc.xl_entry;