-This patch implements the large EA support in ext4. If the size of
-an EA value is larger than the blocksize, then the EA value would
-not be saved in the external EA block, instead it would be saved
-in an external EA inode. So, the patch also helps support a larger
-number of EAs.
+Date: Fri, 8 Oct 2021 12:28:37 +0700
+Subject: [PATCH] This patch implements the large EA support in ext4. If the
+ size of an EA value is larger than the blocksize, then the EA value would not
+ be saved in the external EA block, instead it would be saved in an external
+ EA inode. So, the patch also helps support a larger number of EAs.
-Index: linux-stage/fs/ext4/ext4.h
-===================================================================
---- linux-stage.orig/fs/ext4/ext4.h
-+++ linux-stage/fs/ext4/ext4.h
-@@ -1617,6 +1617,7 @@ static inline void ext4_clear_state_flag
+---
+ fs/ext4/ext4.h | 6 +
+ fs/ext4/extents.c | 3 +-
+ fs/ext4/ialloc.c | 1 -
+ fs/ext4/indirect.c | 3 +-
+ fs/ext4/inline.c | 2 +-
+ fs/ext4/inode.c | 45 +++-
+ fs/ext4/xattr.c | 605 ++++++++++++++++++++++++++++++++++++++++++---
+ fs/ext4/xattr.h | 33 ++-
+ 8 files changed, 645 insertions(+), 53 deletions(-)
+
+diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
+index ba54a96..e28bcdb 100644
+--- a/fs/ext4/ext4.h
++++ b/fs/ext4/ext4.h
+@@ -1617,6 +1617,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
EXT4_FEATURE_INCOMPAT_EXTENTS| \
EXT4_FEATURE_INCOMPAT_64BIT| \
EXT4_FEATURE_INCOMPAT_FLEX_BG| \
/* bitmap.c */
extern unsigned int ext4_count_free(char *bitmap, unsigned numchars);
void ext4_inode_bitmap_csum_set(struct super_block *sb, ext4_group_t group,
-@@ -2233,6 +2238,7 @@ extern void ext4_set_inode_flags(struct
+@@ -2233,6 +2238,7 @@ extern void ext4_set_inode_flags(struct inode *);
extern void ext4_get_inode_flags(struct ext4_inode_info *);
extern int ext4_alloc_da_blocks(struct inode *inode);
extern void ext4_set_aops(struct inode *inode);
extern int ext4_writepage_trans_blocks(struct inode *);
extern int ext4_chunk_trans_blocks(struct inode *, int nrblocks);
extern int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode,
-Index: linux-stage/fs/ext4/inode.c
-===================================================================
---- linux-stage.orig/fs/ext4/inode.c
-+++ linux-stage/fs/ext4/inode.c
-@@ -136,8 +136,6 @@ static void ext4_invalidatepage(struct p
+diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
+index d8434f2..8263aa4 100644
+--- a/fs/ext4/extents.c
++++ b/fs/ext4/extents.c
+@@ -2461,7 +2461,8 @@ int ext4_ext_index_trans_blocks(struct inode *inode, int extents)
+
+ static inline int get_default_free_blocks_flags(struct inode *inode)
+ {
+- if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
++ if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode) ||
++ ext4_test_inode_flag(inode, EXT4_INODE_EA_INODE))
+ return EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET;
+ else if (ext4_should_journal_data(inode))
+ return EXT4_FREE_BLOCKS_FORGET;
+diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
+index a739f71..472bb41 100644
+--- a/fs/ext4/ialloc.c
++++ b/fs/ext4/ialloc.c
+@@ -247,7 +247,6 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
+ * as writing the quota to disk may need the lock as well.
+ */
+ dquot_initialize(inode);
+- ext4_xattr_delete_inode(handle, inode);
+ dquot_free_inode(inode);
+ dquot_drop(inode);
+
+diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c
+index 68163c3..b7cf6e0 100644
+--- a/fs/ext4/indirect.c
++++ b/fs/ext4/indirect.c
+@@ -959,7 +959,8 @@ static int ext4_clear_blocks(handle_t *handle, struct inode *inode,
+ int flags = EXT4_FREE_BLOCKS_VALIDATED;
+ int err;
+
+- if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
++ if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode) ||
++ ext4_test_inode_flag(inode, EXT4_INODE_EA_INODE))
+ flags |= EXT4_FREE_BLOCKS_FORGET | EXT4_FREE_BLOCKS_METADATA;
+ else if (ext4_should_journal_data(inode))
+ flags |= EXT4_FREE_BLOCKS_FORGET;
+diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
+index 0e7433b..b1295be 100644
+--- a/fs/ext4/inline.c
++++ b/fs/ext4/inline.c
+@@ -59,7 +59,7 @@ static int get_max_inline_xattr_value_size(struct inode *inode,
+
+ /* Compute min_offs. */
+ for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
+- if (!entry->e_value_block && entry->e_value_size) {
++ if (!entry->e_value_inum && entry->e_value_size) {
+ size_t offs = le16_to_cpu(entry->e_value_offs);
+ if (offs < min_offs)
+ min_offs = offs;
+diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
+index 6c6ac63..2086792 100644
+--- a/fs/ext4/inode.c
++++ b/fs/ext4/inode.c
+@@ -136,8 +136,6 @@ static void ext4_invalidatepage(struct page *page, unsigned int offset,
unsigned int length);
static int __ext4_journalled_writepage(struct page *page, unsigned int len);
static int ext4_bh_delay_or_unwritten(handle_t *handle, struct buffer_head *bh);
/*
* Test whether an inode is a fast symlink.
-@@ -186,6 +184,8 @@ void ext4_evict_inode(struct inode *inod
+@@ -186,6 +184,8 @@ void ext4_evict_inode(struct inode *inode)
{
handle_t *handle;
int err;
trace_ext4_evict_inode(inode);
-@@ -235,8 +235,8 @@ void ext4_evict_inode(struct inode *inod
+@@ -235,8 +235,8 @@ void ext4_evict_inode(struct inode *inode)
* protection against it
*/
sb_start_intwrite(inode->i_sb);
if (IS_ERR(handle)) {
ext4_std_error(inode->i_sb, PTR_ERR(handle));
/*
-@@ -251,6 +251,32 @@ void ext4_evict_inode(struct inode *inod
+@@ -251,6 +251,32 @@ void ext4_evict_inode(struct inode *inode)
if (IS_SYNC(inode))
ext4_handle_sync(handle);
inode->i_size = 0;
err = ext4_mark_inode_dirty(handle, inode);
if (err) {
-@@ -267,10 +293,10 @@ void ext4_evict_inode(struct inode *inod
+@@ -267,10 +293,10 @@ void ext4_evict_inode(struct inode *inode)
* enough credits left in the handle to remove the inode from
* the orphan list and set the dtime field.
*/
if (err != 0) {
ext4_warning(inode->i_sb,
"couldn't extend journal (err %d)", err);
-@@ -307,6 +333,9 @@ void ext4_evict_inode(struct inode *inod
+@@ -307,6 +333,9 @@ void ext4_evict_inode(struct inode *inode)
ext4_free_inode(handle, inode);
ext4_journal_stop(handle);
sb_end_intwrite(inode->i_sb);
return;
no_delete:
ext4_clear_inode(inode); /* We must guarantee clearing of inode... */
-@@ -5132,7 +5161,7 @@ static int ext4_index_trans_blocks(struc
+@@ -5132,7 +5161,7 @@ static int ext4_index_trans_blocks(struct inode *inode, int lblocks,
*
* Also account for superblock, inode, quota and xattr blocks
*/
int pextents)
{
ext4_group_t groups, ngroups = ext4_get_groups_count(inode->i_sb);
-Index: linux-stage/fs/ext4/xattr.c
-===================================================================
---- linux-stage.orig/fs/ext4/xattr.c
-+++ linux-stage/fs/ext4/xattr.c
-@@ -201,6 +201,7 @@ ext4_xattr_check_names(struct ext4_xattr
+diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
+index c10e37f..2288410 100644
+--- a/fs/ext4/xattr.c
++++ b/fs/ext4/xattr.c
+@@ -201,6 +201,7 @@ ext4_xattr_check_names(struct ext4_xattr_entry *entry, void *end,
while (!IS_LAST_ENTRY(entry)) {
if (entry->e_value_size != 0 &&
(value_start + le16_to_cpu(entry->e_value_offs) <
(void *)e + sizeof(__u32) ||
value_start + le16_to_cpu(entry->e_value_offs) +
-@@ -233,19 +234,26 @@ ext4_xattr_check_block(struct inode *ino
+@@ -233,19 +234,26 @@ ext4_xattr_check_block(struct inode *inode, struct buffer_head *bh)
}
static inline int
{
struct ext4_xattr_entry *entry;
size_t name_len;
-@@ -265,11 +273,109 @@ ext4_xattr_find_entry(struct ext4_xattr_
+@@ -265,11 +273,109 @@ ext4_xattr_find_entry(struct ext4_xattr_entry **pentry, int name_index,
break;
}
*pentry = entry;
}
error = size;
-@@ -346,7 +461,7 @@ ext4_xattr_ibody_get(struct inode *inode
+@@ -346,7 +461,7 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
if (error)
goto cleanup;
error = ext4_xattr_find_entry(&entry, name_index, name,
if (error)
goto cleanup;
size = le32_to_cpu(entry->e_value_size);
-@@ -354,8 +469,16 @@ ext4_xattr_ibody_get(struct inode *inode
+@@ -354,8 +469,16 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
error = -ERANGE;
if (size > buffer_size)
goto cleanup;
}
error = size;
-@@ -600,7 +723,7 @@ static size_t ext4_xattr_free_space(stru
+@@ -600,7 +723,7 @@ static size_t ext4_xattr_free_space(struct ext4_xattr_entry *last,
size_t *min_offs, void *base, int *total)
{
for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) {
size_t offs = le16_to_cpu(last->e_value_offs);
if (offs < *min_offs)
*min_offs = offs;
-@@ -611,11 +734,193 @@ static size_t ext4_xattr_free_space(stru
+@@ -611,11 +734,193 @@ static size_t ext4_xattr_free_space(struct ext4_xattr_entry *last,
return (*min_offs - ((void *)last - base) - sizeof(__u32));
}
/* Compute min_offs and last. */
last = s->first;
-@@ -624,7 +929,7 @@ ext4_xattr_set_entry(struct ext4_xattr_i
+@@ -624,7 +929,7 @@ ext4_xattr_set_entry(struct ext4_xattr_info *i, struct ext4_xattr_search *s)
if ((void *)next >= s->end) {
return -EIO;
}
size_t offs = le16_to_cpu(last->e_value_offs);
if (offs < min_offs)
min_offs = offs;
-@@ -632,15 +937,21 @@ ext4_xattr_set_entry(struct ext4_xattr_i
+@@ -632,15 +937,21 @@ ext4_xattr_set_entry(struct ext4_xattr_info *i, struct ext4_xattr_search *s)
}
free = min_offs - ((void *)last - s->base) - sizeof(__u32);
if (!s->not_found) {
return -ENOSPC;
}
-@@ -654,7 +965,8 @@ ext4_xattr_set_entry(struct ext4_xattr_i
+@@ -654,7 +965,8 @@ ext4_xattr_set_entry(struct ext4_xattr_info *i, struct ext4_xattr_search *s)
s->here->e_name_len = name_len;
memcpy(s->here->e_name, i->name, name_len);
} else {
void *first_val = s->base + min_offs;
size_t offs = le16_to_cpu(s->here->e_value_offs);
void *val = s->base + offs;
-@@ -688,13 +1000,18 @@ ext4_xattr_set_entry(struct ext4_xattr_i
+@@ -688,13 +1000,18 @@ ext4_xattr_set_entry(struct ext4_xattr_info *i, struct ext4_xattr_search *s)
last = s->first;
while (!IS_LAST_ENTRY(last)) {
size_t o = le16_to_cpu(last->e_value_offs);
if (!i->value) {
/* Remove the old name. */
size_t size = EXT4_XATTR_LEN(name_len);
-@@ -708,10 +1025,17 @@ ext4_xattr_set_entry(struct ext4_xattr_i
+@@ -708,10 +1025,17 @@ ext4_xattr_set_entry(struct ext4_xattr_info *i, struct ext4_xattr_search *s)
if (i->value) {
/* Insert the new value. */
s->here->e_value_size = cpu_to_le32(i->value_len);
if (i->value == EXT4_ZERO_XATTR_VALUE) {
memset(val, 0, size);
} else {
-@@ -761,7 +1085,7 @@ ext4_xattr_block_find(struct inode *inod
+@@ -761,7 +1085,7 @@ ext4_xattr_block_find(struct inode *inode, struct ext4_xattr_info *i,
bs->s.end = bs->bh->b_data + bs->bh->b_size;
bs->s.here = bs->s.first;
error = ext4_xattr_find_entry(&bs->s.here, i->name_index,
if (error && error != -ENODATA)
goto cleanup;
bs->s.not_found = error;
-@@ -785,8 +1109,6 @@ ext4_xattr_block_set(handle_t *handle, s
+@@ -785,8 +1109,6 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
#define header(x) ((struct ext4_xattr_header *)(x))
if (s->base) {
ce = mb_cache_entry_get(ext4_xattr_cache, bs->bh->b_bdev,
bs->bh->b_blocknr);
-@@ -802,7 +1124,7 @@ ext4_xattr_block_set(handle_t *handle, s
+@@ -802,7 +1124,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
ce = NULL;
}
ea_bdebug(bs->bh, "modifying in-place");
if (!error) {
if (!IS_LAST_ENTRY(s->first))
ext4_xattr_rehash(header(s->base),
-@@ -853,7 +1175,7 @@ ext4_xattr_block_set(handle_t *handle, s
+@@ -853,7 +1175,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
s->end = s->base + sb->s_blocksize;
}
if (error == -EIO)
goto bad_block;
if (error)
-@@ -997,7 +1319,7 @@ int ext4_xattr_ibody_find(struct inode *
+@@ -997,7 +1319,7 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
/* Find the named attribute. */
error = ext4_xattr_find_entry(&is->s.here, i->name_index,
i->name, is->s.end -
if (error && error != -ENODATA)
return error;
is->s.not_found = error;
-@@ -1015,7 +1337,7 @@ int ext4_xattr_ibody_inline_set(handle_t
+@@ -1015,7 +1337,7 @@ int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode,
if (EXT4_I(inode)->i_extra_isize == 0)
return -ENOSPC;
if (error)
return error;
header = IHDR(inode, ext4_raw_inode(&is->iloc));
-@@ -1039,7 +1361,7 @@ static int ext4_xattr_ibody_set(handle_t
+@@ -1039,7 +1361,7 @@ static int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
if (EXT4_I(inode)->i_extra_isize == 0)
return -ENOSPC;
if (error)
return error;
header = IHDR(inode, ext4_raw_inode(&is->iloc));
-@@ -1075,7 +1397,7 @@ ext4_xattr_set_handle(handle_t *handle,
+@@ -1075,7 +1397,7 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
.name = name,
.value = value,
.value_len = value_len,
};
struct ext4_xattr_ibody_find is = {
.s = { .not_found = -ENODATA, },
-@@ -1140,6 +1462,15 @@ ext4_xattr_set_handle(handle_t *handle,
+@@ -1140,6 +1462,15 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
goto cleanup;
}
error = ext4_xattr_block_set(handle, inode, &i, &bs);
if (error)
goto cleanup;
if (!is.s.not_found) {
-@@ -1186,9 +1517,22 @@ ext4_xattr_set(struct inode *inode, int
+@@ -1186,9 +1517,22 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
const void *value, size_t value_len, int flags)
{
handle_t *handle;
goto retry;
if (error == 0)
error = error2;
-@@ -1222,7 +1566,7 @@ static void ext4_xattr_shift_entries(str
+@@ -1222,7 +1566,7 @@ static void ext4_xattr_shift_entries(struct ext4_xattr_entry *entry,
/* Adjust the value offsets of the entries */
for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) {
new_offs = le16_to_cpu(last->e_value_offs) +
value_offs_shift;
BUG_ON(new_offs + le32_to_cpu(last->e_value_size)
-@@ -1469,21 +1813,135 @@ cleanup:
+@@ -1469,21 +1813,140 @@ cleanup:
}
struct buffer_head *bh = NULL;
+ struct ext4_xattr_ibody_header *header;
+ struct ext4_inode *raw_inode;
-+ struct ext4_iloc iloc;
++ struct ext4_iloc iloc = { .bh = NULL };
+ struct ext4_xattr_entry *entry;
+ int credits = 3, error = 0;
+
+ error = ext4_get_inode_loc(inode, &iloc);
+ if (error)
-+ goto cleanup;
+ goto cleanup;
+ raw_inode = ext4_raw_inode(&iloc);
+ header = IHDR(inode, raw_inode);
+ for (entry = IFIRST(header); !IS_LAST_ENTRY(entry);
+ if (!entry->e_value_inum)
+ continue;
+ if (ext4_expand_ino_array(lea_ino_array,
-+ entry->e_value_inum) != 0) {
-+ brelse(iloc.bh);
++ entry->e_value_inum) != 0)
++ goto cleanup;
++
++ error = ext4_journal_get_write_access(handle, iloc.bh);
++ if (error)
+ goto cleanup;
-+ }
+ entry->e_value_inum = 0;
++ entry->e_value_size = 0;
++ error = ext4_handle_dirty_metadata(handle, inode, iloc.bh);
++ if (error)
++ goto cleanup;
+ }
-+ brelse(iloc.bh);
+
+delete_external_ea:
+ if (!EXT4_I(inode)->i_file_acl) {
+ /* add xattr inode to orphan list */
+ ext4_xattr_inode_orphan_add(handle, inode, credits,
+ *lea_ino_array);
- goto cleanup;
++ goto cleanup;
+ }
bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl);
if (!bh) {
EXT4_ERROR_INODE(inode, "block %llu read error",
-@@ -1496,11 +1954,69 @@ ext4_xattr_delete_inode(handle_t *handle
+@@ -1496,11 +1959,78 @@ ext4_xattr_delete_inode(handle_t *handle, struct inode *inode)
EXT4_I(inode)->i_file_acl);
goto cleanup;
}
+ if (ext4_expand_ino_array(lea_ino_array,
+ entry->e_value_inum) != 0)
+ goto cleanup;
++
++ error = ext4_journal_get_write_access(handle, bh);
++ if (error)
++ goto cleanup;
+ entry->e_value_inum = 0;
++ entry->e_value_size = 0;
++ error = ext4_handle_dirty_metadata(handle, inode, bh);
++ if (error)
++ goto cleanup;
+ }
+
+ /* add xattr inode to orphan list */
cleanup:
brelse(bh);
++ brelse(iloc.bh);
+
+ return error;
+}
}
/*
-@@ -1570,10 +2086,9 @@ ext4_xattr_cmp(struct ext4_xattr_header
+@@ -1570,10 +2100,9 @@ ext4_xattr_cmp(struct ext4_xattr_header *header1,
entry1->e_name_index != entry2->e_name_index ||
entry1->e_name_len != entry2->e_name_len ||
entry1->e_value_size != entry2->e_value_size ||
if (memcmp((char *)header1 + le16_to_cpu(entry1->e_value_offs),
(char *)header2 + le16_to_cpu(entry2->e_value_offs),
le32_to_cpu(entry1->e_value_size)))
-@@ -1657,7 +2172,7 @@ static inline void ext4_xattr_hash_entry
+@@ -1657,7 +2186,7 @@ static inline void ext4_xattr_hash_entry(struct ext4_xattr_header *header,
*name++;
}
__le32 *value = (__le32 *)((char *)header +
le16_to_cpu(entry->e_value_offs));
for (n = (le32_to_cpu(entry->e_value_size) +
-Index: linux-stage/fs/ext4/xattr.h
-===================================================================
---- linux-stage.orig/fs/ext4/xattr.h
-+++ linux-stage/fs/ext4/xattr.h
+diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
+index c767dbd..8312412 100644
+--- a/fs/ext4/xattr.h
++++ b/fs/ext4/xattr.h
@@ -42,7 +42,7 @@ struct ext4_xattr_entry {
__u8 e_name_len; /* length of name */
__u8 e_name_index; /* attribute name index */
};
struct ext4_xattr_search {
-@@ -106,7 +127,13 @@ extern int ext4_xattr_get(struct inode *
+@@ -106,7 +127,13 @@ extern int ext4_xattr_get(struct inode *, int, const char *, void *, size_t);
extern int ext4_xattr_set(struct inode *, int, const char *, const void *, size_t, int);
extern int ext4_xattr_set_handle(handle_t *, struct inode *, int, const char *, const void *, size_t, int);
extern void ext4_xattr_put_super(struct super_block *);
extern int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
-Index: linux-stage/fs/ext4/ialloc.c
-===================================================================
---- linux-stage.orig/fs/ext4/ialloc.c
-+++ linux-stage/fs/ext4/ialloc.c
-@@ -247,7 +247,6 @@ void ext4_free_inode(handle_t *handle, s
- * as writing the quota to disk may need the lock as well.
- */
- dquot_initialize(inode);
-- ext4_xattr_delete_inode(handle, inode);
- dquot_free_inode(inode);
- dquot_drop(inode);
-
-Index: linux-stage/fs/ext4/inline.c
-===================================================================
---- linux-stage.orig/fs/ext4/inline.c
-+++ linux-stage/fs/ext4/inline.c
-@@ -59,7 +59,7 @@ static int get_max_inline_xattr_value_si
-
- /* Compute min_offs. */
- for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
-- if (!entry->e_value_block && entry->e_value_size) {
-+ if (!entry->e_value_inum && entry->e_value_size) {
- size_t offs = le16_to_cpu(entry->e_value_offs);
- if (offs < min_offs)
- min_offs = offs;
-Index: linux-stage/fs/ext4/extents.c
-===================================================================
---- linux-stage.orig/fs/ext4/extents.c
-+++ linux-stage/fs/ext4/extents.c
-@@ -2461,7 +2461,8 @@ int ext4_ext_index_trans_blocks(struct i
-
- static inline int get_default_free_blocks_flags(struct inode *inode)
- {
-- if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
-+ if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode) ||
-+ ext4_test_inode_flag(inode, EXT4_INODE_EA_INODE))
- return EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET;
- else if (ext4_should_journal_data(inode))
- return EXT4_FREE_BLOCKS_FORGET;
-Index: linux-stage/fs/ext4/indirect.c
-===================================================================
---- linux-stage.orig/fs/ext4/indirect.c
-+++ linux-stage/fs/ext4/indirect.c
-@@ -959,7 +959,8 @@ static int ext4_clear_blocks(handle_t *h
- int flags = EXT4_FREE_BLOCKS_VALIDATED;
- int err;
-
-- if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
-+ if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode) ||
-+ ext4_test_inode_flag(inode, EXT4_INODE_EA_INODE))
- flags |= EXT4_FREE_BLOCKS_FORGET | EXT4_FREE_BLOCKS_METADATA;
- else if (ext4_should_journal_data(inode))
- flags |= EXT4_FREE_BLOCKS_FORGET;
+--
+2.30.2
+